要求:输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第k个节点。如果该链表长度小于k,请返回一个长度为 0 的链表。
输入:{1,2,3,4,5},2
返回值:{4,5}
说明:返回倒数第2个节点4,系统会打印后面所有的节点来比较。
输入:{2},8
返回值:{}
#include
#include
struct ListNode* FindKthToTail(struct ListNode* pHead, int k ) {
struct ListNode* cur = pHead;
int n = 0; //记录链表的长度
int n1 = 0; //记录指针移动到哪了
//判断链表的长度,减去后面的个数,就是前面的个数,遍历就可以
while(cur != NULL) {
n++;
cur = cur->next;
}
cur = pHead; //复原cur指针,指向头结点
if ( k > n) { //长度不够,就返回空
return NULL; //空,就是代表空指针
}
while (cur != NULL) { //遍历链表
n1++;
if ( n-k == n1 ) { //到达指定位置
return cur->next;
}
cur = cur->next;
}
return pHead;
}
双指针法,使用两个指针,两个指针之间差距是k,遍历两个指针,当后者指向空,前者就是要返回的指针。
struct ListNode* FindKthToTail(struct ListNode* pHead, int k ) {
// write code here
if(pHead==NULL||k==0) return 0; //空链表和倒数第0个节点都返回0
struct ListNode *p=pHead,*q=pHead; //双指针法,定义两个链表类型的指针,达到进阶的空间复杂度O(1)
for(int i=1;i<k;i++){ //for循环让p和q的距离保持为k
q=q->next;
if(q==NULL) return 0; //如果链表长度不够k,则返回0
}
while(q->next!=NULL){ //p和q同步往后走,让q指向最后一个节点,p则为倒数第k个节点
p=p->next;
q=q->next;
}
return p; //返回p即可
}
要求:输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)。
输入:{1,2,3},{4,5},{6,7}
返回值:{6,7}
说明:第一个参数{1,2,3}代表是第一个链表非公共部分,第二个参数{4,5}代表是第二个链表非公共部分,
最后的{6,7}表示的是2个链表的公共部分,这3个参数最后在后台会组装成为2个两个无环的单链表,且是有公共节点的。
#include
#include
struct ListNode* FindFirstCommonNode(struct ListNode* pHead1, struct ListNode* pHead2 ) {
// write code here
//依次遍历,肯定不行, 公共结点后面的元素肯定是一样的 //不能反转链表
struct ListNode* cur1 = pHead1;
struct ListNode* cur2 = pHead2;
//申请两个指针数组,存下地址
struct ListNode* arr1[1000] = {};
struct ListNode* arr2[1000] = {};
int n1 = 0;
int n2 = 0;
while (cur1 != NULL) {
arr1[n1] = cur1;
n1++;
cur1 = cur1->next; //移动指针
}
while (cur2 != NULL) {
arr2[n2] = cur2;
n2++;
cur2 = cur2->next; //移动指针
}
for (int i = 0; i <= n1; i++) { //从0开始,这样的话,两条链表相同也是可以的
for (int j = 0; j <= n2; j++) {
if (arr1[i] == arr2[j]) {
return arr1[i];
}
}
}
return NULL;
}
使用双指针的方法。这种方法的核心思想是让两个指针从各自链表的头节点出发,然后分别遍历链表。当一个指针到达链表末尾时,将其重定向到另一个链表的头节点,继续遍历。这样,两个指针会分别在两个链表上遍历相同的节点数量,最终会在第一个公共节点相遇。
#include
#include
struct ListNode* FindFirstCommonNode(struct ListNode* pHead1, struct ListNode* pHead2 ) {
struct ListNode* cur1 = pHead1;
struct ListNode* cur2 = pHead2;
while (cur1 != cur2){
if (cur1 == NULL) {
cur1 = pHead2;
}else {
cur1 = cur1->next;
}
if (cur2 == NULL){
cur2 = pHead1;
}else {
cur2 = cur2->next;
}
}
return cur1;
}
要求:判断给定的链表中是否有环。如果有环则返回true,否则返回false。
输入:{3,2,0,-4},1
返回值:true
说明:第一部分{3,2,0,-4}代表一个链表,第二部分的1表示,-4到位置1(注:头结点为位置0),即-4->2存在一个链接,组成传入的head为一个带环的链表,返回true
bool hasCycle(struct ListNode* head) {
// write code here
struct ListNode* cur = head;
//有一个地址被两个指针指向,记录前面的地址吗
//指针数组
struct ListNode* arr[100000] = {};
int i = 0; //指针数组的索引
while (cur != NULL) {
arr[i] = cur; //记录指针的地址
for(int j = 0; j < i;j++){ //遍历数组进行比较
if (cur == arr[j]){
return true;
}
}
cur = cur->next; //移动指针
i++; //索引+1
}
return false;
}
使用快慢指针的方法,也称为龟兔赛跑算法(Floyd’s cycle detection algorithm)。如果一个链表中存在环,那么快指针最终会在某一时刻追上慢指针。
如上图所示,初始指针在相同结点,快指针p2每次移动两步,慢指针移动一步,最终在第五次的移动中,快指针和慢指针又指向相同的结点。
具体代码如下:
bool hasCycle(struct ListNode* head) {
struct ListNode* p1 = head; //声明慢指针
struct ListNode* p2 = head; //声明快指针
// if (p1 == NULL ) {
// return false;
// }
// while (p2->next != NULL && p2->next->next != NULL) {
while (p2 != NULL && p2->next != NULL) {
p1 = p1->next; //慢指针移动一步
p2 = p2->next->next; //快指针移动两步
if (p1 == p2) {
return true;
}
}
return false;
}
注意:p2 != NULL:判断快指针有没有到链表尾部,到了尾部就结束;p2->next != NULL:保证了快指针在每一步都至少向前移动了两步,保证它的下一个结点有值,下下个结点不管是空还是有值,都是可以移动的。这里不用保证p2->next ->next != NULL。