一起刷剑指Offer算法入门题:链表

算法入门 - 链表

3.1 逆序打印链表

从尾到头打印链表 (nowcoder.com)

利用栈反向输出

class Solution {
    
    
public:
    //正向遍历链表,并依次将元素值放入数组中,再逆置数组即相当于逆置打印链表
    vector<int> printListFromTailToHead(ListNode* head) {
    
    
        vector<int> retV;
        while(head != nullptr) {
    
    
            retV.push_back(head->val);
            head = head->next;
        }
        reverse(retV.begin(), retV.end());
        return retV;
    }
};

可以用数组,当然逆序的问题,交给 stack 结构更合适。

利用递归反向输出

class Solution {
    
    
public:
    //单路递归,递归到空指针处递归结束,回溯时向数组插入当前节点的值。
    //不断回溯不断插入,相当于向前遍历反向递归。
    void _PrintList(ListNode* cur, vector<int>& v) {
    
    
        if (cur == nullptr) {
    
     //结束条件
            return;
        }
        _PrintList(cur->next, v); // 向下单路递归
        v.push_back(cur->val); //回溯时插入
    }
    vector<int> printListFromTailToHead(ListNode* head) {
    
    
        vector<int> retV;
        _PrintList(head, retV);
        return retV;
    }
};

单路递归,递归到空指针处递归结束,回溯时向数组插入当前节点的值。

3.2 链表倒数结点

链表中倒数第k个结点 (nowcoder.com)

头尾指针

class Solution {
    
    
public:
    //头尾指针,头指针先走k步,尾指针再开始并一同遍历,直到头指针走到空(左闭右开),尾指针则为倒数第k个节点。
    //[f, r) [3, n) 相当于是个等距的左闭右开的区间。
    // 7 6 5 4 3 2 1 n -- k=3
    // f     r
    //         f     r
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
    
    
        ListNode* rear = pListHead;
        ListNode* front = pListHead;
        //rear先走k步
        while (k--) {
    
    
            if (rear == nullptr) {
    
     // 将k的合法性的判断融入先走k步的步骤中
                return nullptr;
            }
            rear = rear->next;
        }
        //front和rear一同遍历
        while (rear != nullptr) {
    
    
            front = front->next;
            rear = rear->next;
        }
        return front;
    }
};

统计长度再次遍历

class Solution {
    
    
public:
    //通过倒数位置计算正序位置,再正向遍历链表到相应位置。
    ListNode* FindKthToTail(ListNode* pListHead, size_t k) {
    
    
        size_t sz = 0;
        ListNode* cur = pListHead;
        while (cur) {
    
     //统计链表长度
            sz++;
            cur = cur->next;
        } 
        if (k < 1 || k > sz) {
    
     //排除不合法数值
            return nullptr;
        }
        size_t i = sz - k + 1; //计算正序位置

        cur = pListHead;
        while (--i) {
    
     //正向遍历到正确位置
            cur = cur->next;
        }
        return cur;
    }
};

3.3 逆置链表

反转链表 (nowcoder.com)

栈结构模拟

    class Solution {
    
    
    public:
        //利用栈结构先进后出的特性,倒序存储链表节点的值,再正序放入链表中
        ListNode* ReverseList(ListNode* pHead) {
    
    
            if (pHead == nullptr)
                return nullptr;
            stack<int> st;
            ListNode* cur = pHead;
            while (cur != nullptr) {
    
    
                st.push(cur->val);
                cur = cur->next;
            }
            cur = pHead;
            while (!st.empty()) {
    
    
                cur->val = st.top();
                st.pop();
                cur = cur->next;    
            }
            return pHead;
        }
    };

迭代修改链接关系

class Solution {
    
    
public:
    //正向遍历链表,提前保存当前节点的next指针,并修改next指向上一个节点,直至遍历结束
    ListNode* ReverseList(ListNode* pHead) {
    
    
        if (pHead == nullptr) {
    
    
            return nullptr;
        }
        if (pHead->next == nullptr) {
    
    
            return pHead;
        }
        int count = 2;
        ListNode* prev = pHead;
        ListNode* next = pHead;
        ListNode* cur = pHead->next;
        while (cur) {
    
    
            count++;
            next = cur->next; //提前保存下一个节点
            cur->next = prev; //反向链接
            prev = cur; //prev进位
            cur = next; //cur 进位
        }
        cur = prev;
        while (count--) {
    
    
            cur = cur->next;
        }
        cur->next = nullptr; //逆置之后的最后节点的next指针没有置空
        return prev;
    }
};

3.4 合并有序链表

合并两个排序的链表 (nowcoder.com)

迭代

class Solution {
    
    
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
    
    
        ListNode* head = new ListNode(0);
        ListNode* cur = head;
        while (pHead1 && pHead2) {
    
    
            if (pHead1->val < pHead2->val) {
    
     
                cur->next = pHead1;
                pHead1 = pHead1->next; //cur1进位
            }
            else {
    
     
                cur->next = pHead2;
                pHead2 = pHead2->next; //cur2进位
            }
            cur = cur->next; // cur 进位
        }
        //补全剩余
        if (pHead1 != nullptr) {
    
    
            cur->next = pHead1;  
        }
        if (pHead2 != nullptr) {
    
    
            cur->next = pHead2;  
        }
        return head->next;
    }
};

在这里插入图片描述

递归

class Solution {
    
    
public:
    //递归
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
    
    
        if (!pHead1 || !pHead2) {
    
    
            return pHead1 == nullptr ? pHead2 : pHead1;
        }
        ListNode* head = nullptr; //无头
        if (pHead1->val < pHead2->val) {
    
    
            head = pHead1;
            pHead1 = pHead1->next;
        }
        else {
    
    
            head = pHead2;
            pHead2 = pHead2->next;
        }
        //pHead1,pHead2已进位,问题规模缩小。
        head->next = Merge(pHead1, pHead2);
        return head;
    }
};

3.5 删除重复结点

删除链表中重复的结点 (nowcoder.com)

明确出符合条件的左开右闭区间,才方便单链表进行操作。

class Solution {
    
    
public:
    //带头指向首位元素,左指针指向头节点,右指针指向首节点。有个头节点能方便很多
    //满足 r->val != r->next->val,让左右指针向后遍历,致使左指针停在相等元素区间的前一位。右指针指向相等元素区间的首位。
    //满足 r->val == r->next->val,只让右指针向后遍历,直至右指针到相等元素区间的最后一位。
    //这样就构成了(l,r]为相等元素的左开右闭的区间,直接让l->next = r->next 就可以删去该区间。
    //删除后右指针复位到左指针的下一位,即让其进位。以继续判断之后是否该有相等元素区间,直至r指向null。
    ListNode* deleteDuplication(ListNode* pHead) {
    
    
        if (pHead == nullptr || pHead->next == nullptr) {
    
     //排除不合法情况
            return pHead;
        }
        ListNode* head = new ListNode(0); //建立头节点
        head->next = pHead;
        ListNode* l = head; //左指针指向头节点
        ListNode* r = head->next; //右指针指向左指针的下一位
        
        while (r != nullptr) {
    
    
            //1. 将l确定在重复区域的起始位置 (,]
            while (r->next != nullptr && r->val != r->next->val) {
    
     //不相等则指针向后遍历
                l = l->next;
                r = r->next;
            }
            //2. 将r确定在重复区域的末尾位置 (,]
            while (r->next != nullptr && r->val == r->next->val) {
    
     //相等则单独遍历右指针
                r = r->next;
            }
            
            if (l->next != r) {
    
     //有重复节点
                l->next = r->next; //左指针链接区间尾部
            }
            r = r->next; //进位
        }
        return head->next;
    }
};

3.6 链表公共节点

两个链表的第一个公共结点 (nowcoder.com)

class Solution {
    
    
public:
    //利用第一遍循环,判断两个链表是否相交,并统计链表的长度。
    //如果最后一个节点不相等则无相交节点,如果有,就让指针从距尾节点相同的位置起始向后遍历直到节点相同。
    ListNode* FindFirstCommonNode(ListNode* pHead1, ListNode* pHead2) {
    
    
        int length1 = 0, length2 = 0;
        ListNode* cur1 = pHead1, *cur2 = pHead2;
        while (cur1 != nullptr || cur2 != nullptr) {
    
    
            if (cur1) {
    
    
                ++length1;
                cur1 = cur1->next;
            }
            if (cur2) {
    
    
                ++length2;
                cur2 = cur2->next;
            }
        }
        ListNode* more = pHead1, *less = pHead2;
        if (length1 < length2) {
    
    
            more = pHead2, less = pHead1;
        }
        int step = abs(length1 - length2); //差距步
        while (step--) {
    
    
            more = more->next; // 移动链表指针至对应位置
        }
        //同时判定,一并向后遍历
        while (more != nullptr && less != nullptr) {
    
    
            if (more == less) {
    
    
                return more; 
            }
            more = more->next;
            less = less->next;
        }
        return nullptr;
    }
};

猜你喜欢

转载自blog.csdn.net/yourfriendyo/article/details/124540239
今日推荐