剑指offer——链表

剑指offer

一. 链表(8道)

剑指Offer(三):从尾到头打印链表

输入一个链表,按链表从尾到头的顺序返回一个ArrayList。

/*思路:遍历一遍链表,把链表的值存储到数组中,逆序数组。*/
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        vector<int> res;
        while(head){
            res.push_back(head->val);
            head = head->next;
        }
        return vector<int>(res.rbegin(), res.rend());
    }
};

剑指Offer(十四):链表中倒数第k个结点

输入一个链表,输出该链表中倒数第k个结点。

/*思路:遍历一遍链表,求出链表的长度,求倒数第k个结点,即正数第n-k+1个结点,从头结点走n-k步,找到该节点*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        int n = 0;
        for(auto p = pListHead; p; p = p->next) n++;
        auto p = pListHead;
        if(k > n) return nullptr;  // 特判,k超出链表长度范围,返回空指针
        for(int i = 0; i < n-k; i++) p = p->next;
        return p;
    }
};

剑指Offer(十五):反转链表

输入一个链表,反转链表后,输出新链表的表头。

方法一:迭代版本:

/*思路:用一个额外变量记录当前节点的前驱结点,遍历一遍链表,链表不空时,当前节点的下一个点指向前驱,前驱结点后移,当前点往后移(需要记录当前点的下一个点),最后前驱结点即为反转链表的头结点。*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        ListNode *pre = nullptr, *cur = pHead;
        while(cur){
            ListNode *next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
};

方法二:递归版本:

/*思路:1->2->3->4->5->NULL 递归后,5->4->3->2->NULL,再把2指向1,1指向空,递归后的尾指针即为新链表的表头*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {  // 1->2->3->4->5->NULL
        if(!pHead || !pHead->next) return pHead;
        auto tail = ReverseList(pHead->next);  // 5->4->3->2->NULL
        pHead->next->next = pHead;
        pHead ->next = NULL;
        return tail;
    }
};

剑指Offer(十六):合并两个排序的链表

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

/*思路:二路归并算法,每次找到剩余所有数的最小值,放到新链表的后边*/
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        ListNode *dummy = new ListNode(-1), *tail = dummy;
        while(pHead1 && pHead2){
            if(pHead1->val < pHead2->val) tail = tail->next = pHead1, pHead1 = pHead1->next;
            else tail = tail->next = pHead2, pHead2 = pHead2->next;
        }
        if(pHead1) tail->next = pHead1;
        if(pHead2) tail->next = pHead2;
        return dummy->next;
    }
};

剑指Offer(二十五):复杂链表的复制

请实现一个函数可以复制一个复杂链表。在复杂链表中,每个结点除了有一个指针指向下一个结点外,还有一个额外的指针指向链表中的任意结点或者null。

/*思路:1.在每个结点的后边插入一个结点;2.遍历一遍链表,把复制结点的random指针赋值;3.把复制的结点接到新的链表里。*/
class Solution {
public:
    RandomListNode* Clone(RandomListNode* pHead)
    {
        // 在每个结点的后边复制一个结点
        for(auto p = pHead; p;){
            auto np = new RandomListNode(p->label);
            auto next = p->next;
            p->next = np;
            np->next = next;
            p = next;
        }
        // 遍历一遍链表,把random指针赋值
        for(auto p = pHead; p; p = p->next->next){
            if(p->random)
                p->next->random = p->random->next;
        }
        // 把复制结点接到新的链表
        auto dummy = new RandomListNode(-1);
        auto cur = dummy;
        for(auto p = pHead; p; p = p->next){
            cur->next = p->next;
            cur = cur->next;
            p->next = cur->next;  // p要接到原来链表的下一个指针
        }
        return dummy->next;
    }
};

剑指Offer(三十六):两个链表的第一个公共结点

输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)

/*思路:两个指针p和q分别指向两个链表的表头,p和q同时每步往后移,若p先走到空节点,再接着从第二个链表头开始走;若q先走到空节点,再接着从第一个链表头开始走,当p和q相遇时,一定走到第一个公共结点(相交)或者空结点(不相交)*/
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* head1, ListNode* head2) {
        if(!head1 || !head2) return NULL;  // 特判
        auto p = head1, q = head2;
        while(p != q){
            if(p) p = p->next;
            else p = head2;
            if(q) q = q->next;
            else q = head1;
        }
        return p;
    }
};

剑指Offer(五十五):链表中环的入口结点

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

/*思路:定义快慢指针,快指针每次走两步,慢指针每次走一步,无环,返回空,有环,在第一次相遇时,慢指针退回表头,此时,慢指针和快指针同时一起走,当两个指针相遇时,即为环的入口结点*/
// 注:c++中,return NULL、nullptr、0是一样的
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* head)
    {
        if(!head || !head->next) return 0;
        auto i = head, j = head;  // i为慢指针,j为快指针
        while(i && j){
            i = i->next;
            j = j->next;
            if(j) j = j->next;
            if(i == j){  // 有环,一定会相遇
                i = head;
                while(i != j){
                    i = i->next;
                    j = j->next;
                }
                return i;  // 第二次相遇一定是环的入口结点
            }
        }
        return 0;  // 链表为空,无环
    }
};

剑指Offer(五十六):删除链表中重复的结点

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

/*思路:遍历链表里的值,存放在数组里,创建一个新的链表,链表值为数组里次数为1的值*/
class Solution {
public:
    ListNode* deleteDuplication(ListNode* head)
    {
        vector<int> res;
        for(auto p = head; p; p = p->next)
            res.push_back(p->val);
        auto dummy = new ListNode(-1), cur = dummy;
        for(auto i : res){
            if(count(res.begin(), res.end(), i) == 1){
                cur->next = new ListNode(i);
                cur = cur->next;
            }
        }
        return dummy->next;
    }
};

完整程序:

#include <bits/stdc++.h>
using namespace std;
struct ListNode{
    int val;
    struct ListNode *next;
    ListNode(int x):
            val(x), next(nullptr){}
};
ListNode* Reverse(ListNode *head){
    if(!head || !head->next) return head;
    ListNode *pre = nullptr, *cur = head;
    while(cur){
        auto next = cur->next;
        cur->next = pre;
        pre = cur;
        cur = next;
    }
    return pre;
}

int main(){
    auto p1 = new ListNode(1);
    auto p2 = new ListNode(2);
    auto p3 = new ListNode(3);
    auto p4 = new ListNode(4);
    auto p5 = new ListNode(5);
    p1->next = p2;
    p2->next = p3;
    p3->next = p4;
    p4->next = p5;
    p5->next = NULL;
    auto head = Reverse(p1);
    for(auto p = head; p; p = p->next)
        cout << p->val << ' ';
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值