链表

目录

 

2. 两数相加    中等

19. 删除链表的倒数第N个节点    中等

24. ;两两交换链表中的节点    中等

61. 旋转链表    中等

82. 删除排序链表中的重复元素 II    中等

86. 分隔链表    中等

92. 反转链表 II    中等

109. 有序链表转换二叉搜索树    中等


2. 两数相加    中等

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
public ListNode addTwoNumbers (ListNode l1, ListNode l2) {
    ListNode dummy = new ListNode(0);
    ListNode p = dummy;
    int pre = 0;
    while (l1 != null || l2 != null || pre != 0) {
        int num1 = l1 == null ? 0 : l1.val;
        int num2 = l2 == null ? 0 : l2.val;
        int sum = num1 + num2 + pre;
        pre = sum / 10;
        ListNode node = new ListNode(sum % 10);
        node.next = p.next;
        p.next = node;
        p = p.next;
        if (l1 != null) {
            l1 = l1.next;
        }
        if (l2 != null) {
            l2 = l2.next;
        }
    }
    return dummy.next;
}

解析:创建一个新的链表,先创建一个哨兵节点dummy,临时指针p指向dummy,设置进位值pre

当l1不为空或者l2不为空,或进位pre不为0的时候进入while循环,

计算l1与l2的和加上进位得到总数sum,计算当下的进位pre为sum/10,

创建当前节点node,node的当前节点的值为sum%10,

将当前node节点的next指针指向p的next节点,将p的next指针指向当前node节点

p移动到当前节点位置

如果l1不为空,l1移动到l1的下一个节点

如果l2不为空,l2移动到l2的下一个节点
条件满足,继续循环

循环结束后,返回的哨兵节点dummy的下一个节点即为新链表的头结点

 

19. 删除链表的倒数第N个节点    中等

给定一个链表,删除链表的倒数第 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.

说明:

给定的 n 保证是有效的。

进阶:

你能尝试使用一趟扫描实现吗?

public ListNode removeNthFromEnd(ListNode head, int n) {
    ListNode dummy = new ListNode(0);
    dummy.next = head;
    ListNode fast = dummy;
    ListNode slow = dummy;
    for (int i = 0; i <= n; i++) {
        fast = fast.next;
    }
    while (fast != null) {
        fast = fast.next;
        slow = slow.next;
    }
    slow.next = slow.next.next;
    return dummy.next;
}

解析:创建一个哨兵节点dummy,将哨兵dummy的next指针指向链表头结点head

创建快慢指针fast和slow,指向dummy哨兵节点

让快指针fast向后走n+1步

如果快指针指向的节点不为空,则快慢指针同时向后移动,直到快指针fast为空

当快指针fast指向为空的时候,此时慢指针slow的next指针所指向的节点即为需要删除的节点

删除目标节点

返回哨兵节点dummy的下一个节点

 

24. ;两两交换链表中的节点    中等

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例:

给定 1->2->3->4, 你应该返回 2->1->4->3
    //递归
    public ListNode swapPairs(ListNode head) {
        //递归终止条件,如果当前这一对节点为空或只有一个节点,则不需要交换
        if (head == null || head.next == null) {
            return head;
        }
        //当前这一对需要交换的两个节点
        ListNode first = head;
        ListNode second = head.next;

        //第一个节点的next指针指向下一对需要交换节点的头
        first.next = swapPairs(second.next);
        //第二个节点的next指针指向第一个节点
        second.next = first;

        //返回当前这一对节点的新的头
        return second;
    }

解析:每次传入一对节点

确定递归终止条件,如果传入的当前对为空或者只有一个节点,则不需要交换,终止递归

找出当前对中需要进行交换的两个节点

当前第一个节点的next指针指向下一对节点的新头

当前第二个节点的next指针指向当前第一个节点
返回当前节点的新头,即当前对的第二个节点

    //迭代
    public ListNode swapPairs(ListNode head) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode preNode = dummy;
        while (head != null && head.next != null) {
            //找出当前需要交换的两个节点
            ListNode first = head;
            ListNode second = head.next;

            //交换两个节点
            preNode.next = second;
            first.next = second.next;
            second.next = first;

            //将前驱节点preNode的next指针指向更新后当前对节点的最后一个节点
            preNode.next = first;
            //将head节点指向下一对节点的头节点
            head = first.next;
        }
        //返回当前链表的头结点,即哨兵节点dummy的下一个节点
        return dummy.next;
    }

解析:创建一个哨兵节点dummy,哨兵节点dummy的next指针指向链表的头结点head

创建一个preNode指针指向哨兵节点dummy

当当前对有两个节点时,进入循环

找出当前需要交换的两个节点first和second

交换两个节点

更新preNode指针和head指针

当当前对节点为空或者只有一个节点时,退出循环

返回当前链表的头结点,即哨兵节点dummy的下一个节点

 

61. 旋转链表    中等

给定一个链表,旋转链表,将链表每个节点向右移动 个位置,其中 是非负数。

示例 1:

输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL

示例 2:

输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL
    public ListNode rotateRight(ListNode head, int k) {
        //如果没有节点或者只有一个节点,则直接返回
        if (head == null || head.next == null) {
            return head;
        }

        //将原链表收尾相连,形成一个环
        ListNode old_tail = head;
        int n = 1;//n用来确定链表的总长度
        while (old_tail.next != null) {
            old_tail = old_tail.next;
            n++;
        }
        old_tail.next = head;

        //断开链表
        ListNode new_tail = head;
        //将倒数第k个节点之前的链表断开
        for (int i = 0; i < n - k % n - 1; i++) {
            new_tail = new_tail.next;
        }
        ListNode new_head = new_tail.next;
        new_tail.next = null;

        return new_head;
    }

解析:先判断链表节点是否少于两个,如果是,则直接返回

将链表首尾相连,形成一个环形链表,并计算出链表的总长度n

在链表的倒数第k+1个位置和倒数第k个位置断开链表

倒数第k个位置为新链表的头结点

倒数第k+1个位置为新链表的尾节点

 

82. 删除排序链表中的重复元素 II    中等

给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。

示例 1:

输入: 1->2->3->3->4->4->5
输出: 1->2->5

示例 2:

输入: 1->1->1->2->3
输出: 2->3
    public ListNode deleteDuplicates(ListNode head) {
        //如果当前链表的节点数小于两个,则直接返回
        if (head == null || head.next == null) {
            return head;
        }
        //生成一个新的链表,节点dummy作为新链表的哨兵节点
        ListNode dummy = new ListNode(0);
        ListNode tail = dummy;//新链表的尾节点
        for (ListNode l = head, r = head; l != null; l = r) {
            //如果为重复节点,则指针r后移
            while (r != null && r.val == l.val) {
                r = r.next;
            }
            //如果l的下一个节点就是r,则说明当前节点和下一个节点不是重复节点
            if (l.next == r) {
                //尾节点指向l
                tail.next = l;
                //尾节点后移一位,移动到当前链表的尾节点
                tail = l;
                //当前链表的尾节点置空
                tail.next = null;
            }
        }
        //返回新链表的头结点
        return dummy.next;
    }

解析:如果当前链表的节点数目小于两个,则直接返回 (因为此时不可能有重复节点)

生成一个新的链表,节点dummy作为新链表的哨兵节点

生成新链表的尾节点tail,指向哨兵节点

生成左右指针指向head,当左指针不为空的时候,进入循环

当右指针r不为空且与左指针l的值相同时,右指针r不断后移,直到左右指针的值不同为止

如果左指针l的下一个节点不指向右指针r时,则说明他们是相邻的,即当前节点与下一个节点的值不同

此时,尾节点的next指针指向当前左指针l指向的节点

当前尾节点后移一位,也就是当前l的位置

当前尾节点的下一个节点置空,否则可能会出现环

重复上述过程,直到左指针指向为空,退出循环

返回新链表的头结点

 

86. 分隔链表    中等

给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。

你应当保留两个分区中每个节点的初始相对位置。

示例:

输入: head = 1->4->3->2->5->2, x = 3
输出: 1->2->2->4->3->5
    public ListNode partition(ListNode head, int x) {
        ListNode beforeDummy = new ListNode(0);
        ListNode beforeNode = beforeDummy;
        ListNode afterDummy = new ListNode(0);
        ListNode afterNode = afterDummy;
        while (head != null) {
            if (head.val < x) {
                beforeNode.next = head;
                beforeNode = beforeNode.next;
            } else {
                afterNode.next = head;
                afterNode = afterNode.next;
            }
            head = head.next;
        }
        afterNode.next = null;
        beforeNode.next = afterDummy.next;
        return beforeDummy.next;
    }

解析:生成一个前驱哨兵节点链表beforeDummy和一个后继哨兵节点链表afterDummy

如果当前节点小于x,则将当前节点连接到前驱链表后

如果当前节点大于x,则将当前节点连接到后继链表后

当前节点后移

后继链表的next指针置空

前驱链表的next指针指向后继链表头节点

返回前驱链表头结点

 

92. 反转链表 II    中等

反转从位置 mn 的链表。请使用一趟扫描完成反转。

说明:
1 ≤ m ≤ n ≤ 链表长度。

示例:

输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
    public ListNode reverseBetween(ListNode head, int m, int n) {
        //如果链表中少于两个节点,则直接返回
        if (head == null || head.next == null) {
            return head;
        }
        //前驱节点
        ListNode preNode = null;
        //后继节点
        ListNode curNode = head;
        //让preNode走到需要反转的开始节点位置之前一个位置
        //让curNode走到需要反转的开始节点位置
        while (m > 1) {
            preNode = curNode;
            curNode = curNode.next;
            m--;
            n--;
        }
        //con节点为需要开始反转节点的前一个位置
        ListNode con = preNode;
        //tail节点为需要开始反转节点的位置
        ListNode tail = curNode;
        //开始反转链表
        while (n > 0) {
            ListNode next = curNode.next;
            curNode.next = preNode;
            preNode = curNode;
            curNode = next;
            n--;
        }
        if (con != null) {//不是从第一个位置开始反转
            con.next = preNode;
        } else {//从第一个位置开始反转
            head = preNode;
        }
        tail.next = curNode;
        return head;
    }

解析:如果链表中少于两个节点,则直接返回,无需反转

让前驱节点走到需要开始反转的节点之前的一个节点

让当前节点走到需要开始反转的节点

用con指针记录当前前驱节点指针所指向的节点,也即需要反转的链表段的前驱节点

用tail指针记录当前当前节点指针所指向的节点,也即当前需要反转的链表段的第一个节点

然后开始反转

反转结束后

  前驱节点preNode指向的节点为需要反转的链表中的最后一个节点

  当前节点curNode指向的节点为需要反转的链表中的最后一个节点的后继节点

如果不是从第一个节点开始反转,则con的next指针指向需要反转链表中的最后一个节点preNode

如果是从第一个节点开始反转,则head指向需要反转链表中的最后一个节点preNode

将需要反转链表段的第一个节点的next指针指向当前节点,也即需要反转链表段的之后的节点

返回链表的头结点

 

109. 有序链表转换二叉搜索树    中等

给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

示例:

给定的有序链表: [-10, -3, 0, 5, 9],

一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:

      0
     / \
   -3   9
   /   /
 -10  5
    public TreeNode sortedListToBST(ListNode head) {
        //如果链表为空,则直接返回
        if (head == null) {
            return null;
        }
        //找到链表的中间元素
        ListNode mid = findMidElement(head);
        //生成当前树的根节点
        TreeNode node = new TreeNode(mid.val);
        //如果链表中只有一个节点,则直接返回当前树的根节点
        if (head == mid) {
            return node;
        }
        //生成树的左右子树
        node.left = sortedListToBST(head);
        node.right = sortedListToBST(mid.next);
        //返回当前树的根节点
        return node;
    }

    //寻找链表的中间节点
    private ListNode findMidElement(ListNode head) {
        //用于断开链表
        ListNode preNode = null;
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            preNode = slow;
            slow = slow.next;
            fast = fast.next.next;
        }
        if (preNode != null) {
            preNode.next = null;
        }
        return slow;
    }

解析:如果链表为空,则直接返回

寻找链表的中间节点

创建当前这课树的根节点,中间节点的值作为当前这课树的根节点的值
如果链表只有一个节点,则直接返回这棵树的根节点(如果不这样,就会出现无限循环)

递归生成当前这棵树的左右子树

返回当前这棵树的根节点

你可能感兴趣的:(算法,算法,leetcode,链表)