输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
思路1: 使用栈的先入后出的思想,先遍历一遍并存入栈,后面再弹出保存即可
vector<int> printListFromTailToHead(ListNode* head) { // 定义一个栈 stack<int> nodes; // 遍历入栈每个节点的值 ListNode* pNode = head; while (pNode) { nodes.push(pNode->val); pNode = pNode->next; } // 将其放入结果中 vector<int> res; while (!nodes.empty()) { res.push_back(nodes.top()); nodes.pop(); } return res; }
思路2: 使用递归的思路,先达到最深,然后再一步一步操作(取出值),则达到反序的效果。
vector<int> res; vector<int> printListFromTailToHead(ListNode* head) { if(head) { // 先往深处递归 if(head->next!=NULL){ printListFromTailToHead(head->next); } // 再操作(此处为将值取出) res.push_back(head->val); } return res; }
1,删除链表的某个指定节点p
将该节点后一个节点的值复制,再跳过后一个节点即可。
伪代码:
if p->next 不为空: pNext = p->next; p->val = pNext->val; p->next = pNext->next; delete pNext; pNext = nullptr; else if p == pHead : // p不是尾节点且等于头节点,说明只有一个节点。 delete p; p = nullptr; head = nullptr; else: // 是尾节点且不是头节点时,则循环至前一位,再将next置空即可 q = pHead; while (q->next != p) // 循环直至p的前一处 q = q->next; q->next = nullptr; delete p; p = nullptr;
这个做法需要假定需删除的节点p必定存在与head链表中。
2, 删除链表中的重复节点
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
使用递归的方法,
ListNode* deleteDuplication(ListNode* pHead) { // 若链表为空或者只有头节点 if (pHead == nullptr || pHead->next == nullptr) return pHead; // 若当前节点的值与下一个的值一样,即为重复时,取pNode为后指针,往后挪,直至不再重复 // 如 2 2 2 4 5 , pHead为2, pNode至4时跳出循环,再处理后面的部分 if (pHead->val == pHead->next->val) { ListNode* pNode = pHead->next; while (pNode != nullptr && pNode->val == pHead->val) pNode = pNode->next; return deleteDuplication(pNode); } // 当前值与下一处不相同时,则连入该点的->next为下一点的递归结果 else { pHead->next = deleteDuplication(pHead->next); return pHead; } }
输入一个链表,输出该链表中倒数第k个结点。
使用两个指针,首先让pAhead先走k-1步,再让两个指针同时走,直至pAhead到链表尾。此时另一个节点即为求的节点。
用到的next的节点都要注意是否为空。1,判断头节点是否为空;2,在pAhead先行时,随时检测是否超出,即k大于了链表长度。
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) { if (pListHead == nullptr || k==0) // 若k为int则判断k<=0 return nullptr; ListNode* pBehind = pListHead; ListNode* pAhead = pListHead; // 让pAhead先走k-1个。 for (unsigned int i=0; i<k-1; i++) { pAhead = pAhead->next; // 若走到下一步为空 if (pAhead == nullptr) return nullptr; } // 两个指针一起走,直至pAhead走到尾,pBehind即为结果 while(pAhead->next != nullptr) { pAhead = pAhead->next; pBehind = pBehind->next; } return pBehind; }
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
首先确认有环。两个指针一快一慢,一直循环至两者相遇于节点p ==> 有环,或快的节点->next为空 ==> 无环。
确认有环的情况下,如何找入口节点。两种方法:
方法1, 相遇节点p必处于环内,首先循环一遍至回到原位,得到环内共有n个节点,再在链表表头用两个指针,一个先走n步,然后再一起走,相遇节点即为环的入口节点。
方法2, 两个指针,一个在头节点,另一个在相遇节点p,同时移动,相遇点即为环入口节点,证明见下:
ListNode* EntryNodeOfLoop(ListNode* pHead) { // 先确认是否有环,若有环则pAhead/pBehind为重合结点,无环则返回nullptr if (pHead == nullptr || pHead->next == nullptr || pHead->next->next == nullptr) return nullptr; ListNode* pBehind = pHead->next; ListNode* pAhead = pBehind->next; while (pBehind != pAhead) { if (pAhead == nullptr || pAhead->next == nullptr || pAhead->next->next == nullptr) return nullptr; // 走出链表,说明无环 pBehind = pBehind->next; pAhead = pAhead->next->next; } /* // 方法一:拿到重合处的节点,即环内的节点,做一次循环得到环的长度 ListNode* pNode = pAhead->next; int loop_len = 1; while (pNode != pAhead) { pNode = pNode->next; loop_len++; } // 得到环的长度后,使用两个指针,一个先走loop_len步,之后重合处即是环入口 pAhead = pHead; pBehind = pHead; for (int i=0; i<loop_len; i++) pAhead = pAhead->next; while (pAhead != pBehind) { pAhead = pAhead->next; pBehind = pBehind->next; } */ // 方法二,将一个指针重置到头节点处,另一个从相遇处,同时出发,相遇处即为环入口 pBehind = pHead; while (pBehind != pAhead) { pBehind = pBehind->next; pAhead = pAhead->next; } return pAhead; }
输入一个链表,反转链表后,输出新链表的表头。
思路,使用一个指针p2从头节点往后遍历直至为空,每一步时,先使用一个指针p3来记录p2的next,再将p2的next指向上一个节点p1,然后将p2更新至下一节点,同时上一节点p1也更新。
ListNode* ReverseList(ListNode* pHead) { if (pHead == nullptr || pHead->next == nullptr) return pHead; ListNode* p1 = nullptr; ListNode* p2 = pHead; ListNode* p3; while (p2 != nullptr) { p3 = p2->next; // 记录p2的next p2->next = p1; // p2翻转 p1 = p2; // 更新 p2 = p3; // 更新 } return p1; // 最后循环结束时p2为空,故返回的是p1 }
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
每次都等于是找到一个当前两个链表的头节点的最大的节点,可用递归解决。
ListNode* Merge(ListNode* pHead1, ListNode* pHead2) { if (pHead1 == nullptr) return pHead2; if (pHead2 == nullptr) return pHead1; ListNode* pMergeHead = nullptr; if (pHead1->val < pHead2->val) { pMergeHead = pHead1; pMergeHead->next = Merge(pHead1->next, pHead2); } else { pMergeHead = pHead2; pMergeHead->next = Merge(pHead1, pHead2->next); } return pMergeHead; }
输入两个链表,找出它们的第一个公共结点。
先求的两个链表的长度差,然后让长的链表先走,之后在一起走,相同时则为第一个公共节点。
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) { // 先得到两个链表的长度 ListNode* p1 = pHead1; ListNode* p2 = pHead2; int len1 = 0; int len2 = 0; while(p1 != nullptr) { p1 = p1->next; len1++; } while(p2 != nullptr) { p2 = p2->next; len2++; } // 再确定下来长链表和短链表,以及长度差 ListNode* pLong = pHead1; ListNode* pShort = pHead2; int lenDif = len1-len2; if (len2 > len1) { pLong = pHead2; pShort = pHead1; lenDif = len2-len1; } // 长链表先行长度差个节点 for (int i=0; i<lenDif; i++) pLong = pLong->next; // 两个一起走,直至到达公共节点(也可能不存在公共节点,则会走到nullptr处,最后返回的也会是nullptr) while (pLong != pShort) { pLong = pLong->next; pShort = pShort->next; } return pLong; }