目录
一、OJ题目
1、
思路1:将结点放在指定位置之前
思路2:创建空链表
时间复杂度:O(n)
cpptypedef struct ListNode ListNode; struct ListNode * mergewoLists(struct ListNode * list1,struct ListNode * list2) { if(list1 == NULL) { return list2; } if(list2 == NULL) { return list1; } //创建空链表 ListNOde * newHead,*newTail; newHead = newTail = NULL; ListNode * l1 = list1; ListNode * l2 = list2; while(l1 && l2) { if(l1->val < l2->val) { //l1尾插到新链表中 if(newHead == NULL) { newHead = newTail = l1; } else { //链表不为空 newTail->next = l1; newTail = newTail->next; } l1 = l1->next; } else { //l2尾插到新链表中 if(newHead == NULL) { newHead = newTail = l2; } else { //链表不为空 newTail->next = 12; newTail = newTail->next; } l2 = l2->next; } } //要么l1为空,要么l2为空 if(l2) { newTail->next = l2; } if(l1) { newTail->next = l1; } return newHead; }因为代码太过冗余,因此简化为
cpptypedef struct ListNode ListNode; struct ListNode * mergewoLists(struct ListNode * list1,struct ListNode * list2) { if(list1 == NULL) { return list2; } if(list2 == NULL) { return list1; } //创建非空链表 ListNode * newHead,*newTail; newHead = newTail = (ListNode *)malloc(sizeof(ListNode)); ListNode * l1 = list1; ListNode * l2 = list2; while(l1 && l2) { if(l1->val < l2->val) { //l1尾插到新链表中 newTail->next = l1; newTail = newTail->next; l1 = l1->next; } else { //l2尾插到新链表中 newTail->next = l2; newTail = newTail->next; l2 = l2->next; } } //要么l1为空,要么l2为空 if(l2) { newTail->next = l2; } if(l1) { newTail->next = l1; } ListNode * retHead = newHead->next; free(newHead); newHead = NULL; return retHead; }
2、
思路1:投机取巧创建一个数组存放从头遍历下的数据元素
cppbool chkPailndrome(ListNode * A) { //创建数组------900 int arr[900] = {0}; //遍历链表将链表中的值保存在数组中 ListNode * pcur = A; int i = 0; while(pcur) { arr[i++] = pcur->val; pcur = pcur->next; } //判断数组是否为回文结构 int left = 0; int right = i - 1; while(lsft < right) { if(arr[left] != arr[right]) { return false; } left++; right--; } return ture; }这种方法治标不治本
思路2:找链表的中间结点,将中间结点作为新的链表的头结点进行反转链表
cpp//找中间结点 ListNode * middleNode(ListNode * head) { ListNode * slow = *fast; slow = fast = head; while(fast && fast->next) { slow = slow->next; fast = fast->next->next; } return slow; } //反转链表 ListNode * reverseList(ListNode * head) { //链表为空 if(head == NULL) { return head; } //创建三个指针 ListNode * n1,n2,n3; n1 = NULL, n2 = head, n3 = n2->next; while(n2) { n2->next = n1; n1 = n2; n2 = n3; if(n3) { n3 = n3->next; } } return n1; } bool chkPailndrome(ListNode * A) { //中间结点 ListNode * mod = middleNode(A); //反转中间结点之后的链表 ListNode * right = reverseList(mid); //遍历原链表和反转后的链表 ListNode * left = A; while(right) { if(left->val != right->val) { return false; } left = left->next; right = right->next; } return true; }
3、
cppsutrct ListNode { int val; struct ListNode * next; }; typedef struct ListNode ListNode; struct ListNode *getInsertsectionNode(struct ListNode * headA,struct ListNode * headB) { //求两个链表的长度 ListNode * pa = headA; ListNode * pb = headB; int sizeA = 0; int sizeB = 0; while(pa) { sizeA++; pa = pa->next; } while(pb)] { sizeB++; pb = pb->next; } //计算长度差 int gap = abs(sizeA-sizeB); //让长链表先走gap步 ListNode * shortList = headA; ListNode * longList = headB; if(sizeA > sizeB) { longList = headA; shortList = headB; } while(gap--) { longList = longList->next; } //longList shortList在同一起跑线 while(shortList) { if(longList == shortList) { return longList; } shortList = shortList->next; longList = longList->next; } return NULL; }
4、
思路:快慢指针,在环里追逐,若链表带环,快慢指针一定会相遇
cpptypedef struct ListNode ListNode; bool hasCycle(struct ListNode * head) { //快慢指针 ListNode * slow = head; ListNode * fast = head; while(fast && fast->next) { slow = slow->next; fast = fast->next->next; if(slow == fast) { //相遇 return true; } } return false; }为什么在环形链表中快慢指针一定会在环里面一定会相遇
慢指针每次走一步,快指针每次走两步
我们假设fast和slow之间差距n个距离,slow和fast每次走的步数之间的距离就会缩减一步,当在第n次时就会重合
那如果在环形链表中,慢指针每次走一步,但是快指针每次走3,4,5,6,...步,快慢指针在环中是否还会相遇?
我们既设慢指针slow刚入环时,此时slow和fast之间的距离达到最大为N,每次的步数差为2如果N为偶数,之间的步数会随着临近的次数而减少,N、N-2、N-4、...4、2、0,所以会相遇
如果N为奇数,同理,N、N-2、N-4、...3、1、C-1、出现了套圈。(假设一圈为N)
N为奇数:进入下一圈。若C-1为奇数,即C为偶数,一定不会相遇
若C-1为偶数,即C为奇数,一定会相遇
而在上面的假设中快指针走的总路程时慢指针的3倍(3 * 慢指针 = 快指针)设头部结点到入环结点的距离为L,则有头部节点到慢指针的方程为L,头部节点到快结点的方程为L+C-N+nc,因为快指针 = 3 * 慢指针,则有方程3L = L + C - N + nC,即2L = (n + 1)C - N,则C为偶数,N为偶数。若C为奇数,N为奇数,满足C-1为偶数,假设成立
结论:不管快指针不论走了多少步都可以满足再带环来年表中相遇。
但是在编写代码的时候会有额外的步骤引入,设计到快慢指针的算法体重通常习惯使用慢指针走一步快指针走两步的方式
5、
仔细观察的话,可以得出的结论是快慢指针相遇点和头结点到入环起始结点的距离是相等的
cppstruct ListNode { int val; struct ListNode * next; } typedef struct ListNode ListNode; struct ListNode *deteCycle(struct ListNode * head) { //快慢指针 ListNode * slow = head; ListNode * fast = head; while(fast && fast->next) { slow = slow->next; fast = fast->next->next; if(slow == fast) { //找相遇点 //相遇点和头结点到入环第一个结点距离相等 ListNode * pcur = head; //pcur slow/fast while(pcur != slow) { pcur = pcur->next; slow = slow->next; } //pcur == slow return pcur; } } return NULL; }证明:
我们这假设快指针走的路程是慢指针的两倍,头部结点到入环第一个结点的距离为L,快慢指针到入环的第一个结点的距离为x,入环的一圈距离为R,则
2 * slow = fast
slow : L + x
fast : L + x + nR
代入公式得
2(L + x)= L + x + nR
L + x = nR
L = nR - x
L = (n-1)R + R - x
因为R套圈不影响方程两边,则L = R-x 。假设成功,即快慢指针相遇点和头结点到入环起始结点的距离是相等的





