链表类型题目

发布于:2024-03-03 ⋅ 阅读:(54) ⋅ 点赞:(0)

简介

大家好,这里是jiantaoyab,这篇文章给大家带来的是链表相关的题目练习和解析,希望大家能相互讨论进步


链表的常用技巧

  • 画图

画图能更加清晰,方便我们去理解

  • 引入虚拟的头结点

创建新的头结点指向原来的链表,方便处理边界情况

  • 多定义一个变量

多定义一个next就不用考虑先链接谁的问题

  • 快慢双指针
  1. 判断链表中是否有环
  2. 找环的入口
  3. 找链表倒数第n个节点
  • 链表逆序用头插

两数相加

https://leetcode.cn/problems/add-two-numbers/

https://leetcode.cn/problems/lMSNwu/ 两数相加||

原理

在这里插入图片描述

代码

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* new_head = new ListNode(0); //创建哨兵位头结点
        ListNode* tail = new_head;
        int x = 0;//记录进位
    
        ListNode* cur1 = l1, *cur2 = l2;
        while(cur1 || cur2 || x)
        {
            if(cur1)
            {
                x += cur1->val;
                cur1 = cur1->next;
            }
            if(cur2)
            {
                x += cur2->val;
                cur2= cur2->next;
            }
            tail->next = new ListNode(x % 10);
            tail = tail->next;
            x /= 10;
        }
        tail =  new_head->next;
        delete new_head;
        return tail;
        
    }
};

代码||

class Solution {
public:
 ListNode* ReserveList(ListNode* head) {
        ListNode * head= nullptr;
        ListNode * curr = head;
        while(curr) {
            ListNode* next = curr->next;
            curr->next = prev;
            prev = curr;
            curr = next;
        }
        return prev;
    }
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* new_head = new ListNode(0); //创建哨兵位头结点
        ListNode* tail = new_head;
        int x = 0;//记录进位
        l1 = ReserveList(l1);
        l2 = ReserveList(l2);
        ListNode* cur1 = l1, *cur2 = l2;
        while(cur1 || cur2 || x)
        {
            if(cur1)
            {
                x += cur1->val;
                cur1 = cur1->next;
            }
            if(cur2)
            {
                x += cur2->val;
                cur2= cur2->next;
            }
            tail->next = new ListNode(x % 10);
            tail = tail->next;
            x /= 10;
        }
        tail =  new_head->next;
        tail = ReserveList(tail);
        delete new_head;
        return tail;
    }
};

两两交换链表中的节点

https://leetcode.cn/problems/swap-nodes-in-pairs/

代码

  • 递归的方式

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        //终止条件是链表只有一个节点 / 链表中没有节点
        if(head == nullptr || head->next == nullptr) return head;
        ListNode* nnext =  swapPairs(head->next->next);
        ListNode* newhead = head->next;
        newhead->next = head;
        head->next = nnext;
        return newhead;
    }
};
  • 迭代的方式

原理

在这里插入图片描述

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        // 0个和1个节点直接返回就好
       if(head == nullptr || head->next == nullptr)  return head;
       ListNode* newhead = new ListNode(0); //哨兵位头结点
       newhead->next = head;
       ListNode* prev =newhead;
       ListNode* cur = prev->next;
       ListNode* next = cur->next;
       ListNode* nnext = next->next;
       while(cur != nullptr && next != nullptr)
        {
            //交换节点
            prev->next = next;
            next ->next = cur;
            cur ->next = nnext;

            //更新位置
            prev = cur;
            cur = nnext;
            if(cur != nullptr)
             next = cur->next;
            if(next != nullptr)
              nnext = next->next;
            
           
        }
        cur = newhead->next;
        delete newhead;
        return cur;
    }
};

重排链表(重要)


https://leetcode.cn/problems/LGjMqU/

原理

在这里插入图片描述

代码

class Solution {
public:
    void reorderList(ListNode* head) {
        // <=2节点的直接返回
        if(head == nullptr || head->next == nullptr || head->next->next ==nullptr) return ;
        //1.找到链表的中间节点
        ListNode * slow = head, *fast = head;
        while(fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
        }
        //2.将后半部分链表逆置
        ListNode* new_head = new ListNode(0);
        ListNode* cur = slow->next;     
        slow->next = nullptr; //断开链表
        while(cur)
        {
           ListNode* next = cur->next;
           cur->next = new_head->next;
           new_head->next = cur;
           cur = next;
        }
        //3.合并2个链表
        ListNode* ret_head = new ListNode(0);
        ListNode* prev = ret_head;
        ListNode* cur1 =head, *cur2 = new_head->next;
        while(cur1)
        {
           //先放第一个链表
           prev->next = cur1;
           cur1 = cur1->next;
           prev = prev->next;
           //再放第二个链表
           if(cur2)
           {
               prev->next = cur2;
               cur2 = cur2->next;
               prev = prev->next;
           }
        }
        delete new_head;
        delete ret_head;
    }
};

合并 K 个升序链表

https://leetcode.cn/problems/vvXgSW/

代码

class Solution {

    struct cmp 
    {
        bool operator() (const ListNode* l1, const ListNode* l2) 
        {
            return l1->val > l2->val;
        }
    };

public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        //创建小根堆
        priority_queue<ListNode* ,vector<ListNode*>,cmp> heap;

        //让所有链表的头结点加入到小根堆中
        for(auto l :lists)
        {
            if(l) heap.push(l);
        }

        //合并k个有序链表
        ListNode* new_head = new ListNode(0);
        ListNode* prev = new_head;
        //小根堆中还有元素说明还有链表没到nullptr
        while(!heap.empty())
        {
            ListNode* min = heap.top();
            heap.pop();
            prev->next = min;
            prev = min;
            if(min->next) heap.push(min->next);
        }
        prev->next = nullptr;
        prev = new_head->next;
        delete new_head;
        return prev;
    }
};

//自己用vs调试的时候,可以加上下面代码调试一步一步看
int main()
{
	vector<ListNode*> lists = { new ListNode(1, new ListNode(4, new ListNode(5))),
		new ListNode(1, new ListNode(3, new ListNode(4))),
		new ListNode(2, new ListNode(6)) };
	mergeKLists(lists);
}

递归代码

class Solution {
public:
      ListNode* MergeTowList(ListNode* l ,ListNode* r)
      {
          if(l == nullptr) return r;
          if(r == nullptr) return l;
          ListNode new_head ;
          new_head.next = nullptr;
          ListNode* cur1 = l, *cur2 = r, *prev = &new_head ;
          while(cur1 && cur2)
          {
              if(cur1->val >= cur2->val)
              {
                prev = prev->next = cur2;
                cur2 = cur2->next;
              }
              else
              {
                prev = prev->next = cur1;
                cur1 = cur1->next;
              }
          }
          if(cur1) prev->next =cur1;
          if(cur2) prev->next =cur2;
          return new_head.next;
      }
     ListNode* Merge(vector<ListNode*>& lists, int left, int right) 
     {
         if(left > right) return nullptr;
         if(left == right) return lists[left];
         int mid = (left + right) >> 1;
         ListNode* l = Merge(lists, left, mid); 
         ListNode* r = Merge(lists, mid + 1, right); 
        //合并2个有序链表
        return MergeTowList(l,r);
     }
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {      
        return Merge(lists, 0, lists.size() - 1);
    }
};

K 个一组翻转链表

原理

在这里插入图片描述

代码

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
     
        int N = 0;
        ListNode * p = head;
        while(p)
        {
            p = p->next;
            N++;
        }
        N /= k; //划分N组
        ListNode* new_head =  new ListNode(0);
        ListNode* prev = new_head;
        ListNode* cur = head;
        for(int i = 0; i < N; i++)
        {
            ListNode *first = cur;
            for(int j = 0; j < k; j++)
            {            
                ListNode* next = cur->next;
                cur->next = prev->next;
                prev->next = cur;
                cur = next;
            }
            prev = first;
        }
        //把不需要翻转的接上
        prev->next = cur;
        cur = new_head->next;
        delete new_head;
        return cur;
    }
};

网站公告

今日签到

点亮在社区的每一天
去签到