目录
后记:●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!
题目:
【编程题】
1.单链表的增删查改
2.移除链表元素 OJ链接
3.反转一个单链表。 OJ链接
4.链表的中间结点
给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。OJ链接
5.链表中倒数第k个结点
输入一个链表,输出该链表中倒数第k个结点。 OJ链接
6.合并两个有序数组
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。OJ链接
7.CM11 链表分割
编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。OJ链接
8.链表的回文结构。OJ链接
9.相交链表OJ链接
10.环形链表 II
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL OJ链接
11.环形链表 I
给定一个链表,判断链表中是否有环。 OJ链接
12.复制带随机指针的链表
给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。要求返回这个链表的深度拷贝。OJ链接OJ链接
解析:
1.
// SeqList.h #pragma once #include <stdio.h> #include <assert.h> #include <stdlib.h> typedef int SLDateType; typedef struct SeqList { SLDateType* a; size_t size; size_t capacity; // unsigned int }SeqList; // 对数据的管理:增删查改 void SeqListInit(SeqList* ps); void SeqListDestory(SeqList* ps); void SeqListPrint(SeqList* ps); void SeqListPushBack(SeqList* ps, SLDateType x); void SeqListPushFront(SeqList* ps, SLDateType x); void SeqListPopFront(SeqList* ps); void SeqListPopBack(SeqList* ps); // 顺序表查找 int SeqListFind(SeqList* ps, SLDateType x); // 顺序表在pos位置插入x void SeqListInsert(SeqList* ps, size_t pos, SLDateType x); // 顺序表删除pos位置的值 void SeqListErase(SeqList* ps, size_t pos);
// slist.c #include "SList.h" SListNode* BuySListNode(SLTDateType x) { SListNode* node = (SListNode*)malloc(sizeof(SListNode)); node->data = x; node->next = NULL; return node; } void SListPrint(SListNode* plist) { SListNode* cur = plist; while (cur) //while (cur != NULL) { printf("%d->", cur->data); cur = cur->next; } printf("NULL\n"); } void SListPushBack(SListNode** pplist, SLTDateType x) { SListNode* newnode = BuySListNode(x); if (*pplist == NULL) { *pplist = newnode; } else { SListNode* tail = *pplist; while (tail->next != NULL) { tail = tail->next; } tail->next = newnode; } } void SListPopBack(SListNode** pplist) { SListNode* prev = NULL; SListNode* tail = *pplist; // 1.空、只有一个节点 // 2.两个及以上的节点 if (tail == NULL || tail->next == NULL) { free(tail); *pplist = NULL; } else { while (tail->next) { prev = tail; tail = tail->next; } free(tail); tail = NULL; prev->next = NULL; } } void SListPushFront(SListNode** pplist, SLTDateType x) { assert(pplist); // 1.空 // 2.非空 SListNode* newnode = BuySListNode(x); if (*pplist == NULL) { *pplist = newnode; } else { newnode->next = *pplist; *pplist = newnode; } } void SListPopFront(SListNode** pplist) { // 1.空 // 2.一个 // 3.两个及以上 SListNode* first = *pplist; if (first == NULL) { return; } else if (first->next == NULL) { free(first); *pplist = NULL; } else { SListNode* next = first->next; free(first); *pplist = next; } } SListNode* SListFind(SListNode* plist, SLTDateType x) { SListNode* cur = plist; while (cur) { if (cur->data == x) return cur; cur = cur->next; } return NULL; } void SListInsertAfter(SListNode* pos, SLTDateType x) { assert(pos); SListNode* next = pos->next; // pos newnode next SListNode* newnode = BuySListNode(x); pos->next = newnode; newnode->next = next; } void SListEraseAfter(SListNode* pos) { assert(pos); // pos next nextnext SListNode* next = pos->next; if (next != NULL) { SListNode* nextnext = next->next; free(next); pos->next = nextnext; } }
2.
解1:
/* 解题思路:从头节点开始进行元素删除,每删除一个元素,需要重新链接节点 */ struct ListNode* removeElements(struct ListNode* head, int val) { if(head == NULL) return NULL; struct ListNode* cur = head; struct ListNode* prev = NULL; while(cur) { //如果当前节点是需要删除的节点 if(cur->val == val) { //首先保存下一个节点 struct ListNode* next = cur->next; //如果删除的为头节点,更新头节点 //否则让当前节点的前趋节点链接next节点 if(prev == NULL) { head = cur->next; } else { prev->next = cur->next; } //释放当前节点,让cur指向next free(cur); cur = next; } else { //如果cur不是需要删除的节点,则更新prev,cur prev = cur; cur = cur->next; } } return head; }
解2:
struct ListNode* removeElements(struct ListNode* head, int val) { struct ListNode* prev = NULL; struct ListNode* cur = head; while (cur) { if (cur->val != val) { prev = cur; cur = cur->next; } else { struct ListNode* next = cur->next; if (prev == NULL) { free(cur); head = next; cur = next; } else { free(cur); prev->next = next; cur = next; } } } return head; }
3.
解1:
/* 解题思路: 此题一般常用的方法有两种,三指针翻转法和头插法 1. 三指针翻转法 记录连续的三个节点,原地修改节点指向 2. 头插法 每一个节点都进行头插 */ // 三个指针翻转的思想完成逆置 struct ListNode* reverseList(struct ListNode* head) { if(head == NULL || head->next == NULL) return head; struct ListNode* n1, *n2, *n3; n1 = head; n2 = n1->next; n3 = n2->next; n1->next = NULL; //中间节点不为空,继续修改指向 while(n2) { //中间节点指向反转 n2->next = n1; //更新三个连续的节点 n1 = n2; n2 = n3; if(n3) n3 = n3->next; } //返回新的头 return n1; } // 取节点头插的思想完成逆置 struct ListNode* reverseList(struct ListNode* head) { struct ListNode* newhead = NULL; struct ListNode* cur = head; while(cur) { struct ListNode* next = cur->next; //头插新节点,更新头 cur->next = newhead; newhead = cur; cur = next; } return newhead; }
解2:
struct ListNode* reverseList(struct ListNode* head) { struct ListNode* newHead = NULL; struct ListNode* cur = head; while (cur) { struct ListNode* next = cur->next; //头插 cur->next = newHead; newHead = cur; cur = next; } return newHead; }
4.
解1:
/* 解题思路: 通过快慢指针找到中间节点,快指针每次走两步,慢指针每次走一步,当快指针走到结尾的时候,慢指针正好走到中间位置 */ typedef struct ListNode Node; struct ListNode* middleNode(struct ListNode* head){ Node* slow = head; Node* fast = head; while(fast!=NULL && fast->next != NULL) { slow = slow->next; fast = fast->next->next; } return slow; }
解2:
struct ListNode* middleNode(struct ListNode* head) { struct ListNode* slow, * fast; slow = fast = head; while (fast && fast->next) { slow = slow->next; fast = fast->next->next; } return slow; }
5.
解1:
/* 解题思路: 快慢指针法 fast, slow, 首先让fast先走k步,然后fast,slow同时走,fast走到末尾时,slow走到倒数第k个节点。 */ class Solution { public: ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) { struct ListNode* slow = pListHead; struct ListNode* fast = slow; while(k--) { if(fast) fast = fast->next; else return NULL; } while(fast) { slow = slow->next; fast = fast->next; } return slow; } };
解2:
//1.链表中倒数第k个结点 struct ListNode* FindKthToTail(struct ListNode* pListHead, int k) { struct ListNode* slow, * fast; slow = fast = pListHead; //fast先走k步 while (k--) { //k大于链表的长度 if (fast == NULL) { return NULL; } fast = fast->next; } while (fast) { slow = slow->next; fast = fast->next; } return slow; }
6.
解1:
/* 解题思路: 此题可以先创建一个空链表,然后依次从两个有序链表中选取最小的进行尾插操作进行合并。 */ typedef struct ListNode Node; struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){ if(l1 == NULL) return l2; else if(l2 == NULL) return l1; Node* head = NULL, *tail = NULL; //创建空链表 head = tail = (Node*)malloc(sizeof(Node)); tail->next = NULL; while(l1 && l2) { // 取小的进行尾插 if(l1->val < l2->val) { tail->next = l1; tail = tail->next; l1 = l1->next; } else { tail->next = l2; tail = tail->next; l2 = l2->next; } } //剩余元素直接拼接 if(l1) tail->next = l1; else tail->next = l2; Node* list = head->next; free(head); return list; }
解2:
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) { //尾插法 if (list1 == NULL) { return list2; } if (list2== NULL) { return list1; } struct ListNode* head = NULL, * tail = NULL; while (list1 && list2) { if (list1->val < list2->val) { //head和tail最初都是空指针 if (tail == NULL) { head = tail = list1; } else { tail->next = list1; tail = list1; } list1 = list1->next; } else { if (tail == NULL) { head = tail = list2; } else { tail->next = list2; tail = list2; } list2 = list2->next; } } if (list1) { tail->next = list1; } if (list2) { tail->next = list2; } return head; }
7.
解1:
/* 解题思路 创建两个链表,分别存放小于x的节点和大于等于x的节点,分别进行尾插 */ class Partition { public: ListNode* partition(ListNode* pHead, int x) { if(pHead == NULL) return NULL; struct ListNode* lessHead, *lessTail,*greaterHead, *greaterTail; //创建链表表头 lessHead = lessTail = (struct ListNode*)malloc(sizeof(struct ListNode)); greaterHead = greaterTail = (struct ListNode*)malloc(sizeof(struct ListNode)); struct ListNode* cur = pHead; while(cur) { //小于x的尾插到lessTail if(cur->val < x) { lessTail->next = cur; lessTail = lessTail->next; } //大于等于x的尾插到greaterTail else { greaterTail->next = cur; greaterTail = greaterTail->next; } cur = cur->next; } //链接两个链表 lessTail->next = greaterHead->next; greaterTail->next = NULL; //获取表头 pHead = lessHead->next; free(lessHead); free(greaterHead); return pHead; } };
解2:
//2.链表分割—C语言实现 class Partition { public: ListNode* partition(ListNode* pHead, int x) { struct ListNode* lessHead, * lessTail, * greaterHead, * greaterTail; lessHead = lessTail = (struct ListNode*)malloc(sizeof(struct ListNode)); greaterHead = greaterTail = (struct ListNode*)malloc(sizeof(struct ListNode)); lessTail->next = greaterTail->next = NULL; struct ListNode* cur = pHead; while (cur) { if (cur->val < x) { lessTail->next = cur; lessTail = lessTail->next; } else { greaterTail->next = cur; greaterTail = greaterTail->next; } cur = cur->next; } lessTail->next = greaterHead->next; greaterTail->next = NULL; struct ListNode* list = lessHead->next; free(lessHead); //lessHead=NULL; free(greaterHead); //greaterHead=NULL; return list; } };
8.
/* 解题思路: 此题可以先找到中间节点,然后把后半部分逆置,最近前后两部分一一比对,如果节点的值全部相同,则即为回文。 */ class PalindromeList { public: bool chkPalindrome(ListNode* A) { if (A == NULL || A->next == NULL) return true; ListNode* slow, *fast, *prev, *cur, *nxt; slow = fast = A; //找到中间节点 while (fast && fast->next) { slow = slow->next; fast = fast->next->next; } prev = NULL; //后半部分逆置 cur = slow; while (cur) { nxt = cur->next; cur->next = prev; prev = cur; cur = nxt; } //逐点比对 while (A && prev) { if (A->val != prev->val) return false; A = A->next; prev = prev->next; } return true; } }; /* 此题也可以先把链表中的元素值全部保存到数组中,然后再判断数组是否为回文。不建议使用这种解法,因为如果没有告诉链表最大长度,则不能同此解法 */ class PalindromeList { public: bool chkPalindrome(ListNode* A) { // write code here int a[900] = {0}; ListNode* cur = A; int n = 0; //保存链表元素 while(cur) { a[n++] = cur->val; cur = cur->next; } //判断数组是否为回文结构 int begin = 0, end = n-1; while(begin < end) { if(a[begin] != a[end]) return false; ++begin; --end; } return true; } };
9.
/* 解题思路: 此题可以先计算出两个链表的长度,让长的链表先走相差的长度,然后两个链表同时走,直到遇到相同的节点,即为第一个公共节点 */ struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) { int lenA = 0, lenB = 0; struct ListNode* curA = headA, *curB = headB; //计算链表长度 while(curA) { ++lenA; curA = curA->next; } while(curB) { ++lenB; curB = curB->next; } int gap = abs(lenA-lenB); struct ListNode* longList = headA, *shortList = headB; if(lenA < lenB) { longList = headB; shortList = headA; } //让长链表先走几步 while(gap--){ longList = longList->next; } //两个链表同时走,直到遇到相同的节点 while(longList && shortList) { if(longList == shortList) { return longList; } else { longList = longList->next; shortList = shortList->next; } } return NULL; }
10.
/* 解题思路: 如果链表存在环,则fast和slow会在环内相遇,定义相遇点到入口点的距离为X,定义环的长度为C,定义头到入口的距离为L,fast在slow进入环之后一圈内追上slow,则会得知: slow所走的步数为:L + X fast所走的步数为:L + X + N * C 并且fast所走的步数为slow的两倍,故: 2*(L + X) = L + X + N * C 即: L = N * C - X 所以从相遇点开始slow继续走,让一个指针从头开始走,相遇点即为入口节点 */ typedef struct ListNode Node; struct ListNode *detectCycle(struct ListNode *head) { Node* slow = head; Node* fast = head; while(fast && fast->next) { slow = slow->next; fast = fast->next->next; //走到相遇点 if(slow == fast) { // 求环的入口点 Node* meet = slow; Node* start = head; while(meet != start) { meet = meet->next; start = start->next; } return meet; } } return NULL; }
11.
/* 解题思路: 定义快慢指针fast,slow, 如果链表确实有环,fast指针一定会在环内追上slow指针。 */ typedef struct ListNode Node; bool hasCycle(struct ListNode *head) { Node* slow = head; Node* fast = head; while(fast && fast->next) { slow = slow->next; fast = fast->next->next; if(slow == fast) return true; } return false; }
12.
/* 解题思路: 此题可以分三步进行: 1.拷贝链表的每一个节点,拷贝的节点先链接到被拷贝节点的后面 2.复制随机指针的链接:拷贝节点的随机指针指向被拷贝节点随机指针的下一个位置 3.拆解链表,把拷贝的链表从原链表中拆解出来 */ class Solution { public: Node* copyRandomList(Node* head) { // 1.拷贝链表,并插入到原节点的后面 Node* cur = head; while(cur) { Node* next = cur->next; Node* copy = (Node*)malloc(sizeof(Node)); copy->val = cur->val; // 插入 cur->next = copy; copy->next = next; // 迭代往下走 cur = next; } // 2.置拷贝节点的random cur = head; while(cur) { Node* copy = cur->next; if(cur->random != NULL) copy->random = cur->random->next; else copy->random = NULL; cur = copy->next; } // 3.解拷贝节点,链接拷贝节点 Node* copyHead = NULL, *copyTail = NULL; cur = head; while(cur) { Node* copy = cur->next; Node* next = copy->next; // copy解下来尾插 if(copyTail == NULL) { copyHead = copyTail = copy; } else { copyTail->next = copy; copyTail = copy; } cur->next = next; cur = next; } return copyHead; } };