基础数据结构链表以及oj题目

  • 概念

  • 链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链 接次序实现的 。
  • 两种链表

  • /*不带头不循环单向链表*/
    typedef int SLDataType;
    typedef struct SListNode
    {
    	SLDataType val;
    	struct SListNode* next;
    }SListNode;
    
    
  • /*带头双向循环链表*/
    typedef int DListType;
    
    typedef struct ListNode
    {
    	struct ListNode* prev;
    	struct ListNode* next;
    	DListType val;
    }ListNode;
    
  • 几个常见的oj题

  • [翻转链表](206. 反转链表 - 力扣(LeetCode))

  • 思路1:用三个指针,遍历一遍链表,改变指针的朝向

  • // 指针翻转
    class Solution {
    public:
        ListNode* reverseList(ListNode* head) {
            if(head == nullptr)
                return nullptr;
            // 用三个指针,翻转指针方向
            ListNode* cur, *prev, *next;
            cur = head, prev = nullptr, next = head->next;
            while(cur)
            {
                cur->next = prev;
                prev = cur;
                cur = next;
                if(next != nullptr)
                    next = next->next;
            }
            return prev;
        }
    };
    
  • 思路2:使用递归

  • // 递归
    class Solution {
    public:
        ListNode* reverseList(ListNode* head) {
           return recur(head, nullptr);
        }
    private:
        ListNode* recur(ListNode* cur, ListNode* prev)
        {
            if(cur == nullptr)  return prev;
            ListNode* res = recur(cur->next, cur);
            cur->next = prev;
            return res;
        }
    };
    
  • 下面是思路2的递归展开图
  • image.png{:height 705, :width 758}

  • [合并两个有序链表](21. 合并两个有序链表 - 力扣(LeetCode))

  • 思路:用两个指针指向两个链表的开头,取两个链表val小的尾插到新的链表

  • class Solution {
    public:
        ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
            ListNode* l1 = list1, *l2 = list2;
            // 建一个哨兵位
            ListNode* head, *tail;
            head = tail = new ListNode[1];
            tail->next = nullptr;
            while(l1 != nullptr && l2 != nullptr)
            {
                if(l1->val < l2->val)
                {
                    tail->next = l1;
                    l1 = l1->next;
                    tail = tail->next;
                }
                else
                {
                    tail->next = l2;
                    l2 = l2->next;
                    tail = tail->next;
                }
            }
            // 看看是l1没完还是l2没完
            if(l1 != nullptr)
                tail->next = l1;
            if(l2 != nullptr)
                tail->next = l2;
            ListNode* ret = head->next;
            delete[](head);			// 注意:这里要是delete[]
            return ret;
        }
    };
    
  • 启示:遇到链表尾插的题,加上头结点会轻松许多
  • [环形链表](141. 环形链表 - 力扣(LeetCode))

  • 思路:快慢指针:快指针一次走两步,慢指针一次走一步,如果相遇,证明有换

  • class Solution {
    public:
        bool hasCycle(ListNode *head) {
            ListNode* fast, *slow;
            fast = slow = head;
            while(fast && fast->next)
            {
                slow = slow->next;
                fast = fast->next->next;
                if(slow == fast)
                    return true;
            }
            return false;
        }
    };
    
    • 问题:快指针一次走三步,慢指针一次走一步可以吗?
    • 不可以,有一定几率会错过,如下图
    • image.png{:height 317, :width 673}
    • 当fast一次走4步,下图的情况仍然不会相遇
    • image.png{:height 241, :width 468}
  • [环形链表2](142. 环形链表 II - 力扣(LeetCode))

  • 思路:一个指针从开始走,一个指针从相遇点开始走,他们相遇的点就是链表的开头

  • class Solution {
    public:
        ListNode *detectCycle(ListNode *head) {
            ListNode* fast = head, *slow = head, *meet;
            while(fast && fast->next)
            {
                slow = slow->next;
                fast = fast->next->next;
                if(fast == slow)
                {
                    meet = fast;        // 找到第一次相遇的点
                    while(meet != head)
                    {
                        meet = meet->next;
                        head = head->next;
                    }
                    return meet;
                }
            }
            return NULL;
        }
    };
    
  • 为什么会这样呢?下图给出了解释
  • image.png{:height 332, :width 778}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值