链表总结题型

1.两数相加 力扣2

因为此题他们存储数字是链表逆序的,所以直接进行每一位相加即可。从链表头开始,每一位的值相加组成新链表的头,注意进位问题。然后l1与l2进行遍历后,再次相加组成新链表的下一位。重复过程最后返回新链表即可。

class Solution {

        public ListNode addTwoNumbers(ListNode l1, ListNode l2) {

                ListNode head = null;

                ListNode cur = null;

                int carry = 0;

                while (l1 != null || l2 != null) {

                        int n1 = l1 == null ? 0 : l1.val;

                        int n2 = l2 == null ? 0 : l2.val;

                        int sum = n1 + n2 + carry;

                        if (head == null) {

                                head = cur = new ListNode(sum % 10);

                        } else {

                                cur.next = new ListNode(sum % 10);

                                cur = cur.next;

                        }

                        carry = sum / 10;

                        if (l1 != null) {

                                l1 = l1.next;

                        }

                        if (l2 != null) {

                                l2 = l2.next;

                        }

                }

                if (carry > 0) {

                        cur.next = new ListNode(carry);

                }

                return head;

        }

}

2.删除链表的倒数第n个节点

此题。第一步我们要求链表长度,用一个while循环。第二,我们要正向的找到要删除的节点的上一个节点,然后删除他的下一个节点。最后返回链表即可。

class Solution {

        public ListNode removeNthFromEnd(ListNode head, int n) {

                int a = 0;

                ListNode dummy = new ListNode(0,head);

                ListNode cur = dummy;

                dummy.next = head;

                while (head != null) {

                        a++;

                        head = head.next;

                }

                for (int i = 0;i < a - n;i++) {

                        cur = cur.next;

                }

                cur.next = cur.next.next;

                return dummy.next;

        }

}

3.合并两个有序链表 21

此题用递归即可。确定头节点递归后返回即可。

class Solution {

        public ListNode mergeTwoLists(ListNode l1, ListNode l2) {

                if (l1 == null || l2 == null) {

                        return l1 == null ? l2 : l1;

                } else if (l1 == null && l2 == null) {

                        return null;

                } else {

                        if (l1.val >= l2.val) {

                                l2.next = mergeTwoLists(l1,l2.next);

                                return l2;

                        } else {

                                l1.next = mergeTwoLists(l1.next,l2);

                                return l1;

                        }

                }

        }

}

4.两两交换链表中的节点  24

首先,我们要设新节点。用来表示交换的两个节点和他们之后的节点。然后第二个节点->第一个节点->他们之后的节点。然后再将指针节点往后移两位即可。重复步骤最后返回头节点。

class Solution {

        public ListNode swapPairs(ListNode head) {

                ListNode dummy = new ListNode(0,head);

                dummy.next = head;

                ListNode cur = dummy;

                while (cur.next != null && cur.next.next != null) {

                        ListNode temp1 = cur.next;

                        ListNode temp2 = cur.next.next;

                        ListNode temp = temp2.next;

                        cur.next = temp2;

                        temp2.next = temp1;

                        temp1.next = temp;

                        cur = cur.next.next;

                }

                return dummy.next;

        }

}

5.旋转链表 61

旋转链表的题,要把链表成环,这样简单。首先遍历找到最后一个节点,把它和头节点相连成环。然后根据k找到新头节点,断开,返回新头节点即可。

此题两个优化,首先,头节点为空,或者只有头节点或者k为0时,不能旋转,直接返回即可。

第二,当这个给定的k值为倍数时,也等于没旋转,返回头节点即可。

class Solution {

        public ListNode rotateRight(ListNode head, int k) {

                if(head == null || head.next == null || k == 0) {

                        return head;

                }

                int n = 1;

                ListNode cur = head;

                while (cur.next != null) {

                        cur = cur.next;

                        n++;

                }

                int a = n - k % n;

                if (a == n) return head;

                cur.next = head;

                while (a > 0) {

                        a = a - 1;

                        cur = cur.next;

                }

                ListNode node = cur.next;

                cur.next = null;

                return node;

        }

}

6.删除排序链表中的重复元素二。82

此题要求是删除所有重复的元素,删光,所以我们找出了要删的元素,直接利用while循环一直删除即可。

class Solution {

        public ListNode deleteDuplicates(ListNode head) {

                if (head == null || head.next == null) return head;

                ListNode dummy = new ListNode(0,head);

                dummy.next = head;

                ListNode cur = dummy;

                while (cur.next != null && cur.next.next != null) {

                        if (cur.next.val != cur.next.next.val) {

                                cur = cur.next;

                        } else {

                                int k = cur.next.val;

                                //一直删前面的,删光

                                while (cur.next != null && cur.next.val == k) {

                                        cur.next = cur.next.next;

                                }

                        }

                }

                return dummy.next;

        }

}

7.删除排序链表中的元素一 83

此题不用全部删除,所以不用第二层while循环了,因为if的判断条件时判断cur的后两个节点,所以直接删除即可。

class Solution {

        public ListNode deleteDuplicates(ListNode head) {

                if (head == null || head.next == null) return head;

                ListNode dummy = new ListNode(0,head);

                dummy.next = head;

                ListNode cur = dummy;

                while (cur.next != null && cur.next.next != null) {

                        if (cur.next.val != cur.next.next.val) {

                                cur = cur.next;

                        } else {

                                cur.next = cur.next.next;

                        }

                }

                return dummy.next;

        }

}

8.分割链表 86

创建两个新的链表,一个存小的,一个存大的,最后连上就行

class Solution {

           public ListNode partition(ListNode head, int x) {

                ListNode dummy1 = new ListNode(0,head);

                ListNode dummy2 = new ListNode(0,head);

                ListNode cur1 = dummy1;

                ListNode cur2 = dummy2;

                        while (head != null) {

                                if (head.val < x) {

                                        cur1.next = head;

                                        cur1 = cur1.next;

                                } else {

                                        cur2.next = head;

                                        cur2 = cur2.next;

                                }

                                head = head.next;

                        }

                        cur2.next = null;

                        cur1.next = dummy2.next;

                return dummy1.next;

         }

}

9.反转链表一 206

首先,设置一个新pre头节点,把它设为null,然后让头指点指向空,让pre赋值到头节点。然后while循环继续往下遍历重复步骤,最后pre应该到了最后一个节点,此时链表已经反转,并且pre是头节点。

class Solution {

        public ListNode reverseList(ListNode head) {

                if (head == null || head.next == null) return head;

                ListNode cur = head;

                ListNode pre = null;

                while (cur != null) {

                         ListNode next = cur.next;

                        cur.next = pre;

                        pre = cur;

                        cur = next;

                }

                return pre;

        }

}

10.反转链表二 92

根据上一道题的反转列表做法,先设置一个返回新链表的类,然后我们需要找到四个点,分别是,要翻转列表的左右端点,和他们端点的旁边两个节点。通过遍历我们找到了这四个点,然后翻转之后,再把她们重新连接,就实现了反转一部分链表。

class Solution {

        public ListNode reverseBetween(ListNode head, int left, int right) {

                ListNode dummy = new ListNode(0,head);

                dummy.next = head;

                ListNode cur = dummy;

                for (int i = 0;i < left - 1;i++) {

                        cur = cur.next;

                }

                ListNode small = cur.next;

                ListNode l = cur;

                cur = cur.next;

                l.next = null;

                for (int i = 0;i < right - left;i++) {

                        cur = cur.next;

                }

                ListNode large = cur;

                ListNode r = cur.next;

                large.next = null;

                reverse(small);

                l.next = large;

                small.next = r;

                return dummy.next;

        }

        public ListNode reverse(ListNode head) {

                ListNode pre = null;

                ListNode cur = head;

                while (cur != null) {

                        ListNode next = cur.next;

                        cur.next = pre;

                        pre = cur;

                        cur = next;

                }

                return pre;

        }

}

11.相交链表  160

这道题要找出重合的,首先想到的是找重合set集合。于是第一次写了set的方法,只超过25%,估计是用了辅助空间,用空间换时间。

于是换了种想法,用双指针来实现。一个指针从a链表开始走,一个从b链表开始走,两个指针都让他们走完a+b的全程链表。如果他们相交,那么一定会返回。因为,他们走的全程一样,而且他们最后相交的部分全程也一样,换句话说,如果她们重合,他们在重合之前走的路一定是长度相等的。所以,一定能相交返回那个值。

public class Solution {

        public ListNode getIntersectionNode(ListNode headA, ListNode headB) {

                if (headA == null || headB == null) return null;

                ListNode p1 = headA,p2 = headB;

                while (p1 != p2) {

                        p1 = p1 == null ? headB : p1.next;

                        p2 = p2 == null ? headA : p2.next;

                }

                return p1;

        }

}

12 移除链表元素 203

就是删除节点,遍历删除即可。

class Solution {

        public ListNode removeElements(ListNode head, int val) {

                ListNode dummy = new ListNode(0,head);

                dummy.next = head;

                ListNode cur = dummy;

                while (cur.next != null) {

                        if (cur.next.val == val) {

                                cur.next = cur.next.next;

                        } else {

                                cur = cur.next;

                        }

                }

                return dummy.next;

        }

}

13.回文链表 234

此题要考虑复杂度的问题,我觉得用快慢指针较好,用快慢指针来判断链表的中点,然后断开分成两个链表,把后面的反转。反转之后,再利用遍历两个链表来判断回文。

class Solution {

        public boolean isPalindrome(ListNode head) {

                if (head == null || head.next == null) {

                        return true;

                 }

                ListNode slow = head;

                ListNode fast = head;

                while (fast.next != null && fast.next.next != null) {

                        slow = slow.next;

                        fast = fast.next.next;

                }

                ListNode head1 = slow.next;

                slow.next = null;

                ListNode pre = reverse(head1);

                while (pre != null) {

                        if (head.val != pre.val) {

                                return false;

                        }

                        head = head.next;

                        pre = pre.next;

                }

                return true;

        }

        public ListNode reverse(ListNode head) {

                ListNode cur = head;

                ListNode pre = null;

                while (cur != null) {

                        ListNode next = cur.next;

                        cur.next = pre;

                        pre = cur;

                        cur = next;

                }

                return pre;

        }

}

14.删除链表中的节点 237

不能访问头节点,那就把他下一个节点值赋给他,删他下一个节点就ok了

class Solution {

        public void deleteNode(ListNode node) {

                node.val = node.next.val;

                node.next = node.next.next;

        }

}

15.环形链表1  141

此题看到排重,第一想到的肯定是set集合。但是用了辅助数组,第二想的是否能用快慢指针降低空间复杂度,只要快的节点能赶上慢的,就一定环。为什么他们一定能在环中相遇呢,因为快的走两步,慢的走一步,我的想法是,快的-慢的=1,可以理解为快的以一步靠近慢的。所以一定能遇到。

注意,这种龟兔赛跑问题,快指针一定要放在慢指针的前面。因为如果她们都放在head,那么while循环是不会运行的。直接就会返回true。

public class Solution {

        public boolean hasCycle(ListNode head) {

                if (head == null || head.next == null) return false;

                ListNode slow = head;

                ListNode fast = head.next;

                while (fast != slow) {

                        if (fast == null || fast.next == null) {

                                return false;

                        }

                        slow = slow.next;

                        fast = fast.next.next;

                }

                return true;

        }

}

16.环形链表二  142

相交一定是环与链表的焦点,设相交之前长度为a,在环内走了b相交,环剩下的为c,得出:

a+(n+1)b+nc=2(a+b)⟹a=c+(n−1)(b+c)

证明 走了a就等于走了无论绕多少圈,最后加c也就是链表与环的交点

17.有序链表转换为二叉搜索树 109

先设置一个类来寻找链表的重点,因为链表是有序的,天然的二叉搜索树。然后找到中点建立根节点递归左右子树即可。

class Solution {

        public TreeNode sortedListToBST(ListNode head) {

                return build(head,null);

        }

        public TreeNode build(ListNode left,ListNode right) {

                if (left == right) return null;

                ListNode mid = findmid(left,right);

                TreeNode root = new TreeNode(mid.val);

                root.left = build(left,mid);

                root.right = build(mid.next,right);

                return root;

        }

        public ListNode findmid(ListNode left,ListNode right) {

                 ListNode slow = left;

                ListNode fast = left;

                while (fast != right && fast.next != right) {

                        slow = slow.next;

                        fast = fast.next.next;

                }

                return slow;

        }

}

18 将二叉树展开为链表 114

morrirs遍历,将一个节点,他的右子树安装到左子树的最右边节点,然后把新的左子树安装到右子树的位置上,左子树设置为空,然后节点向右遍历,重复以上步骤,最后得到了单支只有右子树的二叉树,就是展开的链表。

class Solution {

        public void flatten(TreeNode root) {

                while (root != null) {

                        if (root.left != null) {

                                TreeNode pre = root.left;

                                while (pre.right != null) {

                                        pre = pre.right;

                                }

                                pre.right = root.right;

                                root.right = root.left;

                                root.left = null;

                        }

                        root = root.right;

                }

        }

}

19.约瑟夫问题

41个人围城一个环,从一开始往后,3次报数死一回,直到死到最后只剩一个人,这个人的初始位置应该是31,代码如下

public class josephquestion {
    private static class Node<T> {
        T item;
        Node next;

        public Node(T item, Node next) {
            this.item = item;
            this.next = next;
        }
    }

    public static void main(String[] args) {
        //解决约瑟夫问题
        Node<Integer> head = null;
        Node<Integer> pre = null;
        //1。构造循环链表
        for (int i = 1; i <= 41; i++) {
            //设置首节点
            if (i == 1) {
                head = new Node<>(i, null);
                pre = head;
                continue;//立即终止后面的语句进入下一层循环
            }
            Node<Integer> newnode = new Node<>(i, null);
            pre.next = newnode;
            pre = newnode;
            if (i == 41) {
                pre.next = head;
            }
        }
        //2 计数器count,模拟报数
        int count = 0;
        Node<Integer> n = head;
        Node<Integer> before = null;
        while (n.next != n) {
            count++;
            if (count == 3) {
                before.next = n.next;
                System.out.println(n.item + ",");
                count = 0;
                n = n.next;
            } else {
                before = n;
                n = n.next;
            }
        }
        System.out.println(n.item);
    }
}

测试后最后三位输出

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值