链表
- 1.链表的基本操作
 - (1)反转链表(206)
 - (2) 合并两个有序链表(21)
 - (3)两两交换链表中的节点(24)
 
- 2.其它链表技巧
 - (1)相交链表(160)
 - (2)回文链表(234)
 
- 3.练习
 - (1)删除排序链表中的重复元素(83)
 - (2)奇偶链表(328)
 - (3)删除链表的倒数第 N 个结点
 - (4)排序链表(148)
 
1.链表的基本操作
(1)反转链表(206)
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
输入:head = []
输出:[]
 
//迭代
public class Solution {
    public static void main(String[] args) {
        ListNode listNode=new ListNode(1,new ListNode(2,new ListNode(3)));
        Solution solution=new Solution();
        ListNode list = solution.reverseList(listNode);
        while (list!=null){
            System.out.print(list.val+" ");
            list=list.next;
        }
    }
    public ListNode reverseList(ListNode head) {
        if (head==null){
            return null;
        }
        ListNode cur=head;//当前节点
        ListNode pre=null;//当前节点前一个节点 初始化为null
        while (cur!=null){//遍历整个链表
            ListNode next=cur.next;//保存下一个节点 作为下一次的当前节点
            cur.next=pre;//当前节点的下一个节点指向之前的节点 实现反转
            pre=cur;//将当前节点作为下一次的前一个节点
            cur=next;
        }
        return pre;
    }
}
//递归
public class Solution {
    public static void main(String[] args) {
        ListNode listNode=new ListNode(1,new ListNode(2,new ListNode(3)));
        Solution solution=new Solution();
        ListNode list = solution.reverseList(listNode);
        while (list!=null){
            System.out.print(list.val+" ");
            list=list.next;
        }
    }
    public ListNode reverseList(ListNode head) {
        if (head==null||head.next==null){
            return head;
        }
        ListNode newHead=reverseList(head.next);
        head.next.next=head;
        head.next=null;
        return newHead;
    }
}
 
(2) 合并两个有序链表(21)
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
输入:l1 = [], l2 = []
输出:[]
输入:l1 = [], l2 = [0]
输出:[0]
 
public class Solution {
    public static void main(String[] args) {
        ListNode list1=new ListNode(1,new ListNode(2,new ListNode(3)));
        ListNode list2=new ListNode(0,new ListNode(3,new ListNode(4)));
        Solution solution=new Solution();
        ListNode list = solution.mergeTwoLists(list1,list2);
        while (list!=null){
            System.out.print(list.val+" ");
            list=list.next;
        }
    }
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode pre=new ListNode(0);//设置一个头节点
        ListNode head=pre;
        while (list1!=null&&list2!=null){
            if (list1.val<= list2.val){
                head.next=list1;
                list1=list1.next;
            }else {
                head.next=list2;
                list2=list2.next;
            }
            head=head.next;
        }
        if (list1!=null){
            head.next=list1;
        }else {
            head.next=list2;
        }
        return pre.next;
    }
}
 
(3)两两交换链表中的节点(24)
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

输入:head = [1,2,3,4]
输出:[2,1,4,3]
输入:head = []
输出:[]
输入:head = [1]
输出:[1]
 
//迭代
public class Solution {
    public static void main(String[] args) {
        ListNode list1=new ListNode(1,new ListNode(2,new ListNode(3,new ListNode(4))));
        Solution solution=new Solution();
        ListNode list = solution.swapPairs(list1);
        while (list!=null){
            System.out.print(list.val+" ");
            list=list.next;
        }
    }
    public ListNode swapPairs(ListNode head) {
        ListNode listNode=new ListNode(-1);//设置一个首部节点 首部节点的下一个节点指向head的头节点
        listNode.next=head;
        ListNode temp=listNode;//标志节点 指向当前要交换的节点的前一个结点
        //当temp后边只有一个节点或者没有节点时 交换结束
        while (temp.next!=null&&temp.next.next!=null){
            //需要交换的两个节点
            ListNode list1=temp.next;
            ListNode list2=temp.next.next;
            //交换两个节点
            temp.next=list2;//标志节点的下一个节点是list2
            list1.next=list2.next;
            list2.next=list1;
            temp=list1;//更换标志节点
        }
        return listNode.next;
    }
}
 
2.其它链表技巧
(1)相交链表(160)
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:


输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
— 请注意相交节点的值不为 1,因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点)
 是不同的节点。换句话说,它们在内存中指向两个不同的位置,而链表 A 和链表 B 中值为 8 的节点 (A 中第三
 个节点,B 中第四个节点) 在内存中指向相同的位置。
 
//集合
public class Solution {
    public static void main(String[] args) {
        ListNode list1=new ListNode(1,new ListNode(2,new ListNode(3,new ListNode(4))));
        ListNode list2=new ListNode(3,new ListNode(2,new ListNode(3,new ListNode(4))));
        Solution solution=new Solution();
        ListNode list = solution.getIntersectionNode(list1,list2);
        while (list!=null){
            System.out.print(list.val+" ");
            list=list.next;
        }
    }
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        //设置一个节点指向headA的头节点 因为题目要求返回后链表的原始结构不变 所以设置
        ListNode tempA=headA;
        ListNode tempB=headB;
        Set<ListNode> set=new HashSet<>();//存储节点
        //将headA的所有节点加入集合
        while (tempA!=null){
            set.add(tempA);
            tempA=tempA.next;
        }
        //在headB中如果找到第一个相等的节点 那么这个节点之后的其他节点都相等 返回这个节点
        while (tempB!=null){
            if (set.contains(tempB)){
                return tempB;
            }
            tempB=tempB.next;
        }
        return null;
    }
}
//双指针
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode tempA=headA;
        ListNode tempB=headB;
        while (tempA!=tempB){
            tempA=tempA==null?headB:tempA.next;
            tempB=tempB==null?headA:tempB.next;
        }
        return tempA;
    }
 
(2)回文链表(234)
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
输入:head = [1,2,2,1]
输出:true
输入:head = [1,2]
输出:false
 
//将值复制到数组后用双指针法
public class Solution {
    public static void main(String[] args) {
        ListNode list1=new ListNode(1,new ListNode(1,new ListNode(2,new ListNode(1))));
        Solution solution=new Solution();
        System.out.println(solution.isPalindrome(list1));
    }
    public boolean isPalindrome(ListNode head) {
        ListNode cur=head;//当前节点
        List<Integer> list=new ArrayList<>();
        //遍历链表 将元素加入list集合
        while (cur!=null){
            list.add(cur.val);
            cur=cur.next;
        }
        //使用两个指针 分别从头和尾开始遍历 判断是否相等
        int left=0;
        int right=list.size()-1;
        while (left<=right){
            //如果不相等 返回false
            if (!list.get(left).equals(list.get(right))){
                return false;
            }
            left++;
            right--;
        }
        return true;
    }
}
//反转后半部分链表然后比较 链表全部反转然后和原链表比较是不可行的
//因为这样的话原链表会只存在一个头节点 后边节点会消失
public class Solution {
    public static void main(String[] args) {
        ListNode list1=new ListNode(1,new ListNode(1,new ListNode(2,new ListNode(1))));
        Solution solution=new Solution();
        System.out.println(solution.isPalindrome(list1));
    }
    public boolean isPalindrome(ListNode head) {
        if (head.next==null){
            return true;
        }
        //得到中心位置
        ListNode half = getHalf(head);
        //反转中心位置之后的链表
        ListNode reversalList = getReversalList(half.next);
        ListNode left=head;
        ListNode mid=reversalList;
        //设置result变量是为了之后有机会将反转的链表复原 否则在判断到不是回文的时候就可以直接返回false
        boolean result=true;
        while (mid!=null){
            if (left.val!= mid.val){
                result=false;
                break;//退出这个while循环
            }
            left=left.next;
            mid=mid.next;
        }
        //还原链表
        half.next=getReversalList(reversalList);
        return result;
    }
    //得到反转链表
    private ListNode getReversalList(ListNode head) {
        ListNode cur=head;//当前节点
        ListNode pre=null;//当前节点的前一个节点
        while (cur!=null){
            ListNode next=cur.next;//保存当前节点的下一个节点
            cur.next=pre;//当前节点的next节点指向前一个节点
            pre=cur;//更新前一个节点
            cur=next;//更新当前节点
        }
        return pre;
    }
    //使用快慢指针得到链表的中心位置
    private ListNode getHalf(ListNode head){
        ListNode slow=head;//慢指针 一次移动一个节点
        ListNode fast=head;//快指针 一次移动两个节点
        while (fast.next!=null&&fast.next.next!=null){
            fast=fast.next.next;
            slow=slow.next;
        }
        //当遍历完链表后 slow指向的位置就是链表的中心位置
        return slow;
    }
}
 
3.练习
(1)删除排序链表中的重复元素(83)
给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。

输入:head = [1,1,2]
输出:[1,2]
输入:head = [1,1,2,3,3]
输出:[1,2,3]
 
public class Solution {
    public static void main(String[] args) {
        ListNode list1=new ListNode(1,new ListNode(1,new ListNode(2,new ListNode(3))));
        Solution solution=new Solution();
        ListNode listNode = solution.deleteDuplicates(list1);
        while (listNode!=null){
            System.out.print(listNode.val+" ");
            listNode=listNode.next;
        }
    }
    public ListNode deleteDuplicates(ListNode head) {
        if (head==null||head.next==null){
            return head;
        }
        ListNode cur=head;//当前节点
        while (cur!=null&&cur.next!=null){
            if (cur.val==cur.next.val){
                cur.next=cur.next.next;//当前节点的next节点指向next的next节点
            }else {//当当前节点的值不等于下一个节点的值时 移动当前节点
                cur=cur.next;
            }
        }
        return head;
    }
}
 
(2)奇偶链表(328)
给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。
第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。
请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。
你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。

输入: head = [1,2,3,4,5]
输出: [1,3,5,2,4]
 
public class Solution {
    public static void main(String[] args) {
        ListNode list1=new ListNode(1,new ListNode(2,new ListNode(3,new ListNode(4,new ListNode(5)))));
        Solution solution=new Solution();
        ListNode listNode = solution.oddEvenList(list1);
        while (listNode!=null){
            System.out.print(listNode.val+" ");
            listNode=listNode.next;
        }
    }
    public ListNode oddEvenList(ListNode head) {
        if (head==null||head.next==null||head.next.next==null){
            return head;
        }
        ListNode even=head.next;//偶数链表
        ListNode curOdd=head;//奇数链表的当前节点
        ListNode curEven=even;//偶数链表得当前节点
        while (curEven!=null&&curEven.next!=null){
            curOdd.next=curEven.next;
            curOdd=curOdd.next;
            curEven.next=curOdd.next;
            curEven=curEven.next;
        }
        //奇数链表的最后一个节点的next节点指向偶数链表的头节点
        curOdd.next=even;
        return head;
    }
}
 
(3)删除链表的倒数第 N 个结点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
输入:head = [1], n = 1
输出:[]
输入:head = [1,2], n = 1
输出:[1]
 
public class Solution {
    public static void main(String[] args) {
        ListNode list1=new ListNode(1,new ListNode(2,new ListNode(3,new ListNode(4,new ListNode(5)))));
        Solution solution=new Solution();
        ListNode listNode = solution.removeNthFromEnd(list1,2);
        while (listNode!=null){
            System.out.print(listNode.val+" ");
            listNode=listNode.next;
        }
    }
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if (head.next==null){
            return null;
        }
        ListNode cur=head;//当前节点
        int count=0;//节点个数
        while (cur!=null){
            cur=cur.next;
            count++;
        }
        int res=count-n+1;//顺数删除的节点位置
        ListNode pre=new ListNode(0,head);
        cur=pre;
        for (int i=1;i<res;i++){
            cur=cur.next;
        }
        //当到需要被删除的节点的前一个节点时 next指向下一个节点的next节点
        cur.next=cur.next.next;
        return pre.next;
    }
}
//栈
public class Solution {
    public static void main(String[] args) {
        ListNode list1=new ListNode(1,new ListNode(2,new ListNode(3,new ListNode(4,new ListNode(5)))));
        Solution solution=new Solution();
        ListNode listNode = solution.removeNthFromEnd(list1,2);
        while (listNode!=null){
            System.out.print(listNode.val+" ");
            listNode=listNode.next;
        }
    }
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if (head.next==null){
            return null;
        }
        ListNode pre=new ListNode(0,head);
        Deque<ListNode> stack=new LinkedList<>();
        ListNode cur=pre;
        //元素入栈
        while (cur!=null){
            stack.push(cur);
            cur=cur.next;
        }
        //弹出栈顶的n个元素
        for (int i=1;i<=n;i++){
            stack.poll();
        }
        //当前栈顶元素是需要删除元素的前一个元素 则将其的next指向需要删除元素的下一个元素
        stack.peek().next=stack.peek().next.next;
        return pre.next;
    }
}
//双指针
public class Solution {
    public static void main(String[] args) {
        ListNode list1=new ListNode(1,new ListNode(2,new ListNode(3,new ListNode(4,new ListNode(5)))));
        Solution solution=new Solution();
        ListNode listNode = solution.removeNthFromEnd(list1,2);
        while (listNode!=null){
            System.out.print(listNode.val+" ");
            listNode=listNode.next;
        }
    }
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if (head.next==null){
            return null;
        }
        ListNode pre=new ListNode(0,head);
        ListNode first=head;//第一个指针 比第二个指针提前n个节点 当它到null时 第二个指针刚好是需要删除的节点
        ListNode second=pre;
        for (int i=1;i<=n;i++){
            first=first.next;
        }
        //遍历直到第一个节点指向空
        while (first!=null){
            first=first.next;
            second=second.next;
        }
        second.next=second.next.next;
        return pre.next;
    }
}
 
(4)排序链表(148)
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

输入:head = [4,2,1,3]
输出:[1,2,3,4]
输入:head = []
输出:[]
 
//自底向上归并排序 空间复杂度O(1)
public class Solution {
    public static void main(String[] args) {
        ListNode list1=new ListNode(2,new ListNode(1,new ListNode(4,new ListNode(3,new ListNode(0)))));
        Solution solution=new Solution();
        ListNode listNode = solution.sortList(list1);
        while (listNode!=null){
            System.out.print(listNode.val+" ");
            listNode=listNode.next;
        }
    }
    public ListNode sortList(ListNode head) {
        if (head==null){
            return null;
        }
        //计算链表的长度
        int length=getLength(head);
        //加一个前置节点 是为了避免head.next=null的特殊情况的判断
        ListNode preHead=new ListNode(0,head);
        for (int subLength=1;subLength<length;subLength<<=1){
            ListNode pre=preHead;//用于记录每一次不同subLength的已排序的链表的尾位置
            ListNode cur=preHead.next;//用于记录拆分链表的位置
            while (cur!=null){
                //需要排序的第一个子链表 长度为subLength
                ListNode head1=cur;
                for (int i=1;i<subLength&&cur.next!=null;i++){
                    cur=cur.next;
                }
                //需要排序的第二个子链表 长度为subLength
                ListNode head2=cur.next;
                cur.next=null;//将前一个子链表与后边的断开
                cur=head2;//第二个链表头重新赋值给cur
                for (int i=1;i<subLength&&cur!=null&&cur.next!=null;i++){
                    cur=cur.next;
                }
                //断开第二个子链表与后边链表的连接
                ListNode next=null;//记录已经排好序的子链表的结束位置
                if (cur!=null){
                    next = cur.next;//下一个需要排序的子链表的初始位置
                    cur.next=null;//断开连接
                }
                //给两个子链表排序  将每一次排好序的链表合并到pre后边
                pre.next=getMergeList(head1,head2);
                while (pre.next!=null){
                    pre=pre.next;//将指针移动到已排序的链表末尾
                }
                cur=next;//下一个需要排序的子链表的前一个位置
            }
        }
        return preHead.next;
    }
    //合并两个有序链表
    private ListNode getMergeList(ListNode head1, ListNode head2) {
        ListNode preHead=new ListNode(0);
        ListNode cur=preHead;//当前合并链表的尾节点
        while (head1!=null&&head2!=null){
            if (head1.val<= head2.val){
                cur.next=head1;
                head1=head1.next;
            }else {
                cur.next=head2;
                head2=head2.next;
            }
            cur=cur.next;//移动尾节点
        }
        if (head1!=null){
            cur.next=head1;
        }else if (head2!=null){
            cur.next=head2;
        }
        return preHead.next;
    }
    //得到链表长度
    private int getLength(ListNode head){
        int length=0;
        while (head!=null){
            head=head.next;
            length++;
        }
        return length;
    }
}
//自顶向下归并排序 空间复杂度log(n)
public class Solution {
    public static void main(String[] args) {
        ListNode list1=new ListNode(2,new ListNode(1,new ListNode(4,new ListNode(3,new ListNode(0)))));
        Solution solution=new Solution();
        ListNode listNode = solution.sortList(list1);
        while (listNode!=null){
            System.out.print(listNode.val+" ");
            listNode=listNode.next;
        }
    }
    public ListNode sortList(ListNode head) {
        return rec(head,null);
    }
    private ListNode rec(ListNode head, ListNode tail) {
        if (head==null){
            return null;
        }
        if (head.next==tail){
            head.next=null;
            return head;
        }
        //得到当前链表的中心位置的节点
        ListNode mid=getHalf(head,tail);
        ListNode list1=rec(head,mid);//递归将链表前半部分排好序
        ListNode list2=rec(mid,tail);//递归将链表前后部分排好序
        ListNode list=getMergeList(list1,list2);
        return list;
    }
    //得到当前链表的中心位置
    private ListNode getHalf(ListNode head,ListNode tail) {
        ListNode slow=head;//慢指针 一次走一个节点
        ListNode fast=head;//快指针 一次走两个节点
        while (fast!=tail){
            slow=slow.next;
            fast=fast.next;
            if (fast!=tail){
                fast=fast.next;
            }
        }
        return slow;//链表的中心位置
    }
    //合并两个有序链表
    private ListNode getMergeList(ListNode head1, ListNode head2) {
        ListNode preHead=new ListNode(0);
        ListNode cur=preHead;//当前合并链表的尾节点
        while (head1!=null&&head2!=null){
            if (head1.val<= head2.val){
                cur.next=head1;
                head1=head1.next;
            }else {
                cur.next=head2;
                head2=head2.next;
            }
            cur=cur.next;//移动尾节点
        }
        if (head1!=null){
            cur.next=head1;
        }else if (head2!=null){
            cur.next=head2;
        }
        return preHead.next;
    }
}
                









![C#中[]的几种用法](https://img-blog.csdnimg.cn/c9f6bb41a0f24f06b985b645b89348c6.png)







