[LC优选算法#16] 链表 | 两数相加 | 两两交换链表中的节点 | 重排链表

1. 链表的常用技巧和操作

常用技巧:

  1. 链表的难点在于改变指针的指向时机 ,因此做链表类型的题时,画图模拟是非常必要的。
  2. 适当引入虚拟头节点,可以避免过多的分类讨论,降低复杂性的同时还有助于处理边界情况。
  3. 快慢双指针:常用于判断链表是否带环,找带环链表的入口,找倒数第n个节点......
  4. 大大方方定义变量 ,不要吝惜空间。如图,在定义next变量后则可以忽略指针指向的先后顺序。

常用操作:

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
 
//1. 创建值为0的节点
//方式1:堆上分配(指针)
ListNode* newnode = new ListNode(0);
// 类型:ListNode*
// 生命周期:手动 delete 释放
// 用途:链表节点通常用这种方式

//方式2:栈上分配(对象)
ListNode head(0);
// 类型:ListNode
// 生命周期:作用域结束自动销毁
// 用途:临时对象或局部使用

//2. 尾插newnode(cur所在位置为尾节点)
cur->next = newnode;
cur = cur->next;

//3. 头插newnode(newhead为虚拟头节点)
cur = newhead->next;
newhead->next = newnode;
newnode->next = cur;

2. 例题分析

2.1 两数相加

两数相加

解题思路:

模拟两数相加的过程。因为两个链表都是逆序存储数字的,即两个链表的个位数、⼗位数等都已经对应,可以直接相加。过程中需要注意是否产生进位

  1. 过程中需要将进位和链表数字一同相加
  2. 相加结束后若还需进位则需要new一个节点
cpp 复制代码
class Solution {
public:
    typedef ListNode Node;
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2)
    {
        Node* cur1 = l1;
        Node* cur2 = l2;

        Node* newhead = new Node(0);
        Node* prev = newhead;
        int next = 0;

        while(cur1 || cur2)
        {
            int sum = next;
            if(cur1)
            {
                sum += cur1->val;
                cur1 = cur1->next;
            }

            if(cur2)
            {
                sum += cur2->val;
                cur2 = cur2->next;
            }

            Node* tmp = new Node(sum % 10);
            prev->next = tmp;
            prev = tmp;

            next = sum / 10;
        }

        if(next != 0)
        {
            Node* tmp = new Node(next);
            prev->next = tmp;
        }

        Node* ret = newhead->next;
        delete newhead;
        return ret;
    }
};

2.2 两两交换链表中的节点

两两交换链表中的节点

解题思路:

模拟交换链表的过程,这里需要定义四个变量prev, c1, c2, next,循环交换直至链表结尾。注意在交换之后,对应指针的指向已经改变,更新指针时应以交换后的指针为准。

cpp 复制代码
class Solution {
public:
    typedef ListNode Node;

    ListNode* swapPairs(ListNode* head)
    {
        if(!head || !head->next) return head;

        Node* newhead = new Node(0);
        Node* prev = newhead;
        newhead->next = head;
        Node* c1 = head;
        Node* c2 = head->next;
        Node* next = c2->next;

        while(c1 && c2)
        {
            c1->next = next;
            c2->next = c1;
            prev->next = c2;

            if(!next || !next->next)
            {
                break;
            }

            prev = c1;
            c1 = next;
            c2 = c1->next;
            next = c2->next;
        }

        Node* ret = newhead->next;
        delete newhead;
        return ret;
    }
};

2.3 重排链表

重排链表

解题思路:

  1. 中间节点(快慢双指针)
  2. 中间部分往后的节点逆序(双指针 / 头插)
  3. 合并两个链表(双指针)
cpp 复制代码
class Solution {
public:
    typedef ListNode Node;

    ListNode* swapPairs(ListNode* head)
    {
        if(!head || !head->next) return head;

        Node* newhead = new Node(0);
        Node* prev = newhead;
        newhead->next = head;
        Node* c1 = head;
        Node* c2 = head->next;
        Node* next = c2->next;

        while(c1 && c2)
        {
            c1->next = next;
            c2->next = c1;
            prev->next = c2;

            if(!next || !next->next)
            {
                break;
            }

            prev = c1;
            c1 = next;
            c2 = c1->next;
            next = c2->next;
        }

        Node* ret = newhead->next;
        delete newhead;
        return ret;
    }
};

// 本期内容就到这里啦,如果对你有帮助,请三连支持!我是青云,我们下期见^_~