链表常见题目总结

1.从尾到头打印链表

题目描述:

输入一个链表,返回一个反序的链表

实现代码:

class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        stack<int> nodes;
        vector<int> result;
        ListNode* node = head;
        while(node != NULL){
            nodes.push(node->val);
            node = node->next;
        }
        
        while(!nodes.empty()){
            result.push_back(nodes.top());
            nodes.pop();
        }
        return result;
    }
};

2.链表中倒数第k个节点

题目描述:
输入一个链表,输出该链表中倒数第k个结点。
实现代码:
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        if(pListHead == NULL || k == 0){
            return NULL;
        }
        ListNode *pAhead = pListHead;
        ListNode *pBehind = pListHead;
        for(unsigned int i = 0; i < k - 1; i++){
            if(pAhead->next != NULL){
                pAhead = pAhead->next;
            }
            else{
                return NULL;
            }
        }
        while(pAhead->next != NULL){
            pAhead = pAhead->next;
            pBehind = pBehind->next;
        }
        return pBehind;
    }
};

3.反转链表

题目描述:
输入一个链表,反转链表后,输出链表的所有元素。
实现代码:
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        ListNode* pReversedHead = NULL;
        ListNode* pNode = pHead;
        ListNode* pPrev = NULL;
        while(pNode != NULL){
            ListNode* pNext = pNode->next;
            if(pNext == NULL){
                pReversedHead = pNode;
            }
            pNode->next = pPrev;
            pPrev = pNode;
            pNode = pNext;
        }
        return pReversedHead;
    }
};

4.合并两个排序链表

题目描述:
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
实现代码:
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        //判断指针是否为空
        if(pHead1 == NULL){
            return pHead2;
        }
        else if(pHead2 == NULL){
            return pHead1;
        }
        ListNode* pMergedHead = NULL;
        if(pHead1->val < pHead2->val){
            pMergedHead = pHead1;
               pMergedHead->next = Merge(pHead1->next, pHead2);
        }
        else{
            pMergedHead = pHead2;
               pMergedHead->next = Merge(pHead1, pHead2->next);
        }
        return pMergedHead;
    }
};

5.复杂链表的复制

题目描述:
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
实现代码:
class Solution {
public:
    
    //第一步,复制复杂指针的label和next
    void CloneNodes(RandomListNode* pHead){
        RandomListNode* pNode = pHead;
        while(pNode != NULL){
            RandomListNode* pCloned = new RandomListNode(0);
            pCloned->label = pNode->label;
            pCloned->next = pNode->next;
            pCloned->random = NULL;
            
            pNode->next = pCloned;
            pNode = pCloned->next;
        }
    }
    
    //第二步,处理复杂指针的random
    void ConnectSiblingNodes(RandomListNode* pHead){
        RandomListNode* pNode = pHead;
        while(pNode != NULL){
            RandomListNode* pCloned = pNode->next;
            if(pNode->random != NULL){
                pCloned->random = pNode->random->next;
            }
            pNode = pCloned->next;
        }
    }
    
    //第三步,拆分复杂指针
    RandomListNode* ReconnectNodes(RandomListNode* pHead){
        RandomListNode* pNode = pHead;
        RandomListNode* pClonedHead = NULL;
        RandomListNode* pClonedNode = NULL;
        
        if(pNode != NULL){
            pClonedHead = pClonedNode = pNode->next;
            pNode->next = pClonedNode->next;
            pNode = pNode->next;
        }
        
        while(pNode != NULL){
            pClonedNode->next = pNode->next;
            pClonedNode = pClonedNode->next;
            pNode->next = pClonedNode->next;
            pNode = pNode->next;
        }
        return pClonedHead;
    }
    
    RandomListNode* Clone(RandomListNode* pHead)
    {
        CloneNodes(pHead);
        ConnectSiblingNodes(pHead);
        return ReconnectNodes(pHead);
    }
};

6.两个链表的第一个公共节点

问题描述:
输入两个链表,找出它们的第一个公共结点。
实现代码:
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        // 如果有一个链表为空,则返回结果为空
        if(pHead1 == NULL || pHead2 == NULL){
            return NULL;
        }
        // 获得两个链表的长度
        unsigned int len1 = GetListLength(pHead1);
        unsigned int len2 = GetListLength(pHead2);
        // 默认 pHead1 长, pHead2短,如果不是,再更改
        ListNode* pHeadLong = pHead1;
        ListNode* pHeadShort = pHead2;
        int LengthDif = len1 - len2;
        // 如果 pHead1 比 pHead2 小
        if(len1 < len2){
            ListNode* pHeadLong = pHead2;
            ListNode* pHeadShort = pHead1;
            int LengthDif = len2 - len1;
        }
        // 将长链表的前面部分去掉,使两个链表等长
        for(int i = 0; i < LengthDif; i++){
            pHeadLong = pHeadLong->next;
        }
        
        while(pHeadLong != NULL && pHeadShort != NULL && pHeadLong != pHeadShort){
            pHeadLong = pHeadLong->next;
            pHeadShort = pHeadShort->next;
        }
        return pHeadLong;
    }
private:
    // 获得链表长度
    unsigned int GetListLength(ListNode* pHead){
        if(pHead == NULL){
            return 0;
        }
        unsigned int length = 1;
        while(pHead->next != NULL){
            pHead = pHead->next;
            length++;
        }
        return length;
    }
};

7.链表中环的入口节点

题目描述:
一个链表中包含环,请找出该链表的环的入口结点。
实现代码:
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        if(pHead == NULL){
            return NULL;
        }
        ListNode* meetingnode = MeetingNode(pHead);
        if(meetingnode == NULL){
            return NULL;
        }
        // 回环链表结点个数
        int nodesloop = 1;
        // 找到环中结点个数
        ListNode* pNode1 = meetingnode;
        while(pNode1->next != meetingnode){
            pNode1 = pNode1->next;
            nodesloop++;
        }
        pNode1 = pHead;
        // 第一个指针向前移动nodesloop步
        for(int i = 0; i < nodesloop; i++){
            pNode1 = pNode1->next;
        }
        // 两个指针同时移动,找到环入口
        ListNode* pNode2 = pHead;
        while(pNode1 != pNode2){
            pNode1 = pNode1->next;
            pNode2 = pNode2->next;
        }
        return pNode1;
    }
private:
    // 使用快慢指针,找到任意的一个环中结点
    ListNode* MeetingNode(ListNode* pHead){
        ListNode* pSlow = pHead->next;
        if(pSlow == NULL){
            return NULL;
        }
        ListNode* pFast = pSlow->next;
        while(pFast != NULL && pSlow != NULL){
            if(pFast == pSlow){
                return pFast;
            }
            pSlow = pSlow->next;
            pFast = pFast->next;
            if(pFast != NULL){
                pFast = pFast->next;
            }
        }
        return NULL;
    }
};

8.删除链表中重复节点

题目描述:
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5。
实现代码:
class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead)
    {
        if(pHead == NULL){
            return NULL;
        }
        // 指向当前结点前最晚访问过的不重复结点
        ListNode* pPre = NULL;
        // 指向当前处理的结点
        ListNode* pCur = pHead;
        // 指向当前结点后面的结点
        ListNode* pNext = NULL;
        
        while(pCur != NULL){
            // 如果当前结点与下一个结点相同
            if(pCur->next != NULL && pCur->val == pCur->next->val){
                pNext = pCur->next;
                // 找到不重复的最后一个结点位置
                while(pNext->next != NULL && pNext->next->val == pCur->val){
                    pNext = pNext->next;
                }
                // 如果pCur指向链表中第一个元素,pCur -> ... -> pNext ->...
                // 要删除pCur到pNext, 将指向链表第一个元素的指针pHead指向pNext->next。
                if(pCur == pHead){
                    pHead = pNext->next;
                }
                // 如果pCur不指向链表中第一个元素,pPre -> pCur ->...->pNext ->...
                // 要删除pCur到pNext,即pPre->next = pNext->next
                else{
                    pPre->next = pNext->next;
                }
                // 向前移动
                pCur = pNext->next;
            }
            // 如果当前结点与下一个结点不相同
            else{
                pPre = pCur;
                pCur = pCur->next;
            }
        }
        return pHead;
    }
};

9.两数相加

题目描述:
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

实现代码:
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode *result = new ListNode(0);
        ListNode *temp = result;
        int sum = 0;
        while(l1||l2)
        {
            if(l1)
            {
                sum = sum + l1->val;
                l1 = l1->next;
            }
            if(l2)
            {
                sum = sum + l2->val;
                l2 = l2->next;
            }
            temp -> next = new ListNode(sum % 10);
            sum = sum/10;
            temp = temp->next;
        }
        if (sum)
            temp->next = new ListNode(1);
        return result->next;
        
        
    }
};

10.删除链表的倒数第N个节点

题目描述:
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.

说明:

给定的 n 保证是有效的。

进阶:

你能尝试使用一趟扫描实现吗?

实现代码:
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode *pre = new ListNode(0);
        pre->next = head;
        ListNode *p = pre;
        ListNode *q = pre;
        for(int i = 0; i < n; i++)
        {
            p = p->next;
        }
        while(p->next != NULL)
        {
            p = p->next;
            q = q->next;
        }
        ListNode *k = q->next;
        if(k == head)
        {
            head = head->next;
            delete k;
            return head;
        }
        q->next = k->next;
        delete k;
        return head;
    }
};

11.合并k个有序链表

题目描述:
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6
实现代码:
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if (lists.empty()) return NULL;
        int n = lists.size();
        while (n > 1) {
            int k = (n + 1) / 2;
            for (int i = 0; i < n / 2; ++i) {
                lists[i] = mergeTwoLists(lists[i], lists[i + k]);
            }
            n = k;
        }
        return lists[0];
    }
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode *dummy = new ListNode(-1), *cur = dummy;
        while (l1 && l2) {
            if (l1->val < l2->val) {
                cur->next = l1;
                l1 = l1->next;
            } else {
                cur->next = l2;
                l2 = l2->next;
            }
            cur = cur->next;
        }
        if (l1) cur->next = l1;
        if (l2) cur->next = l2;
        return dummy->next;
    }
};

12.两两交换链表中的节点

题目描述:
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

 

示例:

给定 1->2->3->4, 你应该返回 2->1->4->3.

实现代码:
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if(!head||!head->next) return head;//空链和一个元素的链直接返回
        ListNode* l=head;//定义需要交换的左右节点
        ListNode* r=l->next;
        ListNode* p;
        l->next=r->next;//交换第一二个节点
        r->next=l;
        head=r;//首节点赋值为第二个节点
        p=l;//p赋值为第一对交换成功的右节点
        while(l->next&&l->next->next){//如果剩余节点为0或1则返回head,否则进入循环
            l=l->next;//转换到下一节点对
            r=l->next;
            l->next=r->next;//交换节点l,r
            r->next=l;
            p->next=r;
            p=l;
        }
        return head;  
        }
        
};

13.旋转链表

题目描述:
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。

示例 1:

输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL

示例 2:

输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL

实现代码:
class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
          if (!head) return NULL;
          int n = 0;
          ListNode *cur = head;
          while (cur) {
              ++n;
              cur = cur->next;
         }
         k %= n;
         ListNode *fast = head, *slow = head;
         for (int i = 0; i < k; ++i) {
             if (fast) fast = fast->next;
         }
         if (!fast) return head;
         while (fast->next) {
             fast = fast->next;
             slow = slow->next;
         }
         fast->next = head;
         fast = slow->next;
         slow->next = NULL;
         return fast;
    }
};

14.输出链表中的重复元素

题目描述:
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

示例 1:

输入: 1->1->2
输出: 1->2

示例 2:

输入: 1->1->2->3->3
输出: 1->2->3
实现代码:
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode* cur = head;
        while(cur != NULL){
            ListNode* rear = cur->next;
            if(rear == NULL)
                return head;
            if(cur->val == rear->val)
                cur->next = rear->next;
            else
                cur = cur->next;
        }
        return head;
    }
};

15.分隔链表

题目描述:
给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。

你应当保留两个分区中每个节点的初始相对位置。

示例:

输入: head = 1->4->3->2->5->2, x = 3
输出: 1->2->2->4->3->5

实现代码:
class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        if(head == NULL) return head; ListNode less_head(0);
        ListNode more_head(0);
        ListNode* less_ptr = &less_head;
        ListNode* more_ptr = &more_head;
        ListNode* cur = head;
        while(cur != NULL){
            if(cur->val < x){
                less_ptr->next = cur;
                less_ptr = less_ptr->next;
            } else{
                more_ptr->next = cur;
                more_ptr = more_ptr->next;
            } cur = cur->next;
        }
        less_ptr->next = more_head.next;
        more_ptr->next = NULL;
        return less_head.next;

    }
};

16.反转链表Ⅱ

题目描述:
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。

说明:
1 ≤ m ≤ n ≤ 链表长度。

示例:

输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL

实现代码:
class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int m, int n) {
        if (head == NULL || head->next == NULL) {
            return head;
        }
        
        ListNode* dummy = new ListNode(0);
        dummy->next = head;
        
        ListNode* prev = dummy;
        for (int i = 0; i < m - 1; i++) {
            prev = prev->next;
        }
        ListNode* start = prev->next;
        ListNode* then = start->next;
        
        for (int i = 0; i < n - m; i++) {
            start->next = then->next;
            then->next = prev->next;
            prev->next = then;
            then = start->next;
        }
        
        return dummy->next;
    }
};

17.环形链表

题目描述:
给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

实现代码:
class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode *L1,*L2;
        L1=head;
        L2=head;
        while(L1!=NULL && L2 != NULL && L1->next != NULL)
        {
            L1 = L1->next->next;
            L2 = L2->next;
            if(L1 == L2)
                return true;
        }
        return false;
    }
};

18.移除链表元素

题目描述:
删除链表中等于给定值 val 的所有节点。

示例:

输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5

实现代码:
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
while ((head!=NULL)&&(head->val == val))
    {
        head = head->next;
    }
    ListNode* pCurrent = head;
    if (pCurrent == NULL) return NULL;
    while (pCurrent->next!=NULL)
    {
        if (pCurrent->next->val == val)
        {
            pCurrent->next = pCurrent->next->next;
        }
        else
            pCurrent = pCurrent->next;
    }
    return head;
    }
};

19.回文链表

题目描述:
请判断一个链表是否为回文链表。

示例 1:

输入: 1->2
输出: false

示例 2:

输入: 1->2->2->1
输出: true

实现代码:
class Solution {
public:
    bool isPalindrome(ListNode* head) {
                int n=len(head);
        if(n==1)return true;
        ListNode*head2=head;
        for(int i=0;i<(n/2+n%2);i++){
            head2=head2->next;
        }
        head2=reverseList(head2);
        while(head2!=NULL){
            if(head->val!=head2->val) return false;
            else{head=head->next;head2=head2->next;}
        }
        return true;
    }
    int len(ListNode*head){
        int n=0;
        while(head!=NULL){
            head=head->next;
            ++n;
        }
        return n;
    }
    ListNode* reverseList(ListNode* head) {
        ListNode*pt=head;
        ListNode*temp;
        if(pt==NULL) return head;
        while(pt->next!=NULL){
            temp=pt->next;
            pt->next=pt->next->next;
            temp->next=head;
            head=temp;
        }
        return head;
    }
};

20.链表的中间节点

题目描述:
给定一个带有头结点 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

 

示例 1:

输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.

示例 2:

输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。

实现代码:
class Solution {
public:
    ListNode* middleNode(ListNode* head) {
        ListNode*cur=head->next;
        ListNode*prev=head;
        while(cur) {
            if(cur->next) {
                cur=cur->next->next;
            } else {
                cur=cur->next;
            } prev=prev->next;
        }
        return prev;
    }
};
posted @ 2019-08-20 22:05  小菜鸡的刨坑路  阅读(430)  评论(0编辑  收藏  举报