数据结构与算法XS班-左程云第四节课笔记(链表问题)

第4节 链表问题

#####今天迟了,比预想速度慢很多,主要是得动手练习…

##这是数据结构与算法新手班-左程云第四节课的笔记##

单链表

  • 给定一个单链表的头head,完成链表的逆序调整

    class Test{
    
        public static class Node{
            public int value;
            public Node next;
            public Node(int v){
                value = v;
            }
        }
    
        // 传进来的是head地址的一个备份,因为在Java里面都是按值传递
        public static Node reverseLinkedList(Node head){
            Node pre = null;
            Node next = null;
            while(head != null){
                // 先把下面这个结点记住
                next = head.next;
                // 让next变成原先的前面
                head.next = pre;
                // 往后挪动
                pre = head;
                head = next;
            }
            return pre;
        }
    
        public static void main(String[] args){
            // JVM会释放什么样的结点呢?
            // JVM会释放掉那些不可达的结点。比如head -> A -> B <- C,JVM会把C结点释放掉
            Node node1 = new Node(1);
            node1.next = new Node(2);
            node1.next.next = new Node(3);
            node1 = reverseLinkedList(node1);
            // 这段是打印呗!
            while(node1 != null){
                System.out.println(node1.value + " ");
                node1 = node1.next;
            }
            System.out.println();
        }
    }
    
  • 给定一个双链表的头head,完成链表的逆序调整
    在这里插入图片描述

    class Test{
    
        public static class Node{
            public int value;
            public Node last;
            public Node next;
            public Node(int v){
                value = v;
            }
        }
    
        // 传进来的是head地址的一个备份,因为在Java里面都是按值传递
        public static Node reverseDoubleLinkedList(Node head){
            Node pre = null;
            Node next = null;
            while(head != null){
                // 先把下面这个结点记住
                next = head.next;
                // 让next变成原先的前面
                head.next = pre;
                head.last = next;
                // 往后挪动
                pre = head;
                head = next;
            }
            return pre;
        }
    
        public static void main(String[] args){
            // JVM会释放什么样的结点呢?
            // JVM会释放掉那些不可达的结点。比如head -> A -> B <- C,JVM会把C结点释放掉
            Node node1 = new Node(1);
            node1.next = new Node(2);
            node1.next.next = new Node(3);
            node1 = reverseDoubleLinkedList(node1);
            // 这段是打印呗!
            while(node1 != null){
                System.out.println(node1.value + " ");
                node1 = node1.next;
            }
            System.out.println();
        }
    }
    
  • 单链表实现队列(时间复杂度0(1))

    import java.util.LinkedList;
    import java.util.Queue;
    
    class Test{
    
        public static class Node<V>{
            public V value;
            public Node<V> next;
    
            public Node(V v){
                value = v;
                next = null;
            }
        }
    
        // 定义一个队列
        public static class MyQueue<V> {
            private Node<V> head;
            private Node<V> tail;
            private int size;
            public MyQueue(){
                head = null;
                tail = null;
                size = 0;
            }
            public boolean isEmpty(){
                return size == 0;
            }
            public int size(){
                return size;
            }
            // 载入值
            public void offer(V value){
                Node<V> cur = new Node<V>(value);
                if(tail == null){
                    head = cur;
                    tail = cur;
                }else{
                    tail.next = cur;
                    tail = cur;
                }
                size++;
            }
            // 弹出值,C++和C的同学记得析构,因为是手动释放的
            public V poll(){
                V ans = null;
                if(head != null){
                    ans = head.value;
                    head = head.next;
                    size--;
                }
                if(head == null){
                    tail = null;
                }
                return ans;
            }
            public V peek(){
                V ans = null;
                if(head != null){
                    ans = head.value;
                }
                return ans;
            }
        }
    
        public static void main(String[] args){
            MyQueue<Integer> myQueue = new MyQueue<>();
            Queue<Integer> test = new LinkedList<>();
            int testTime = 5000000;
            int maxValue = 200000000;
            System.out.println("测试开始!");
            for (int i = 0; i < testTime; i++) {
                if (myQueue.isEmpty() != test.isEmpty()) {
                    System.out.println("Oops!");
                }
                if (myQueue.size() != test.size()) {
                    System.out.println("Oops!");
                }
                double decide = Math.random();
                if (decide < 0.33) {
                    int num = (int) (Math.random() * maxValue);
                    myQueue.offer(num);
                    test.offer(num);
                } else if (decide < 0.66) {
                    if (!myQueue.isEmpty()) {
                        int num1 = myQueue.poll();
                        int num2 = test.poll();
                        if (num1 != num2) {
                            System.out.println("Oops!");
                        }
                    }
                } else {
                    if (!myQueue.isEmpty()) {
                        int num1 = myQueue.peek();
                        int num2 = test.peek();
                        if (num1 != num2) {
                            System.out.println("Oops!");
                        }
                    }
                }
            }
            if (myQueue.size() != test.size()) {
                System.out.println("Oops!");
            }
            while (!myQueue.isEmpty()) {
                int num1 = myQueue.poll();
                int num2 = test.poll();
                if (num1 != num2) {
                    System.out.println("Oops!");
                }
            }
            System.out.println("测试结束!");
        }
    }
    
  • 单链表实现栈(时间复杂度0(1))

    class Test{
    
        public static class Node<V>{
            public V value;
            public Node<V> next;
    
            public Node(V v){
                value = v;
                next = null;
            }
        }
    
        // 定义一个栈
        public static class MyStack<V> {
            private Node<V> top;
            private int size;
            public MyStack(){
                top = null;
                size = 0;
            }
            public boolean isEmpty(){
                return size == 0;
            }
            public int size(){
                return size;
            }
            // 载入值
            public void offer(V value){
                Node<V> cur = new Node<V>(value);
                if(top == null){
                    top = cur;
                }else{
                    cur.next = top;
                    top = cur;
                }
                size++;
            }
            // 弹出值
            public V poll(){
                V ans = null;
                if(top != null){
                    ans = top.value;
                    top = top.next;
                    size--;
                }
    
                return ans;
            }
            public V peek(){
                V ans = null;
                if(top != null){
                    ans = top.value;
                }
                return ans;
            }
        }
    
        public static void main(String[] args){
            MyStack<Integer> queue = new MyStack<>();
            queue.offer(1);
            queue.offer(2);
            queue.offer(3);
            queue.poll();
            queue.poll();
            System.out.println(queue.size);
            System.out.println();
        }
    }
    

双链表

  • 用双链表结构实现双端队列(什么是双端队列呢?同时允许两头进行进和出操作)

    import java.util.LinkedList;
    import java.util.Queue;
    
    /* 要想实现这个效果,只能用双链表结构实现,为什么?我们分析一下! */
    // 单链表结构只允许头部进行加减,尾部只可以加可以减,但是减操作很难,得遍历呀!!满足不了o(1)了!
    // 双链表就可以了!
    
    class Test{
    
        public static class Node<V>{
            public V value;
            public Node<V> last;
            public Node<V> next;
    
            public Node(V v){
                value = v;
                last = null;
                next = null;
            }
        }
    
        // 定义一个队列
        public static class MyDeque<V> {
            private Node<V> head;
            private Node<V> tail;
            private int size;
            public MyDeque(){
                head = null;
                tail = null;
                size = 0;
            }
            public boolean isEmpty(){
                return size == 0;
            }
            public int size(){
                return size;
            }
            // 从头部加
            public void pushHead(V value){
                Node<V> cur = new Node<>(value);
                if(head != null){
                    cur.next = head;
                    head.last = cur;
                    head = cur;
                }else{
                    // 之前没元素
                    head = cur;
                    tail = cur;
                }
            }
            // 从尾部加
            public void pushTail(V value){
                Node<V> cur = new Node<>(value);
                if(tail != null){
                    tail.next = cur;
                    cur.last = tail;
                    tail = cur;
                }else{
                    // 之前没元素
                    head = cur;
                    tail = cur;
                }
            }
            // 从头部弹
            public V pollHead(){
                V ans = null;
                if(head == null){
                    return ans;
                }
                // 不管是1个还是几个,都执行这一句
                ans = head.value;
                if(head != tail){
                    head = head.next;
                    head.last = null;
                }else{
                    // 只有一个
                    head = null;
                    tail = null;
                }
                size--;
                return ans;
            }
            // 从尾部弹
            public V pollTail(){
                V ans = null;
                if(tail == null){
                    return ans;
                }
                // 不管是1个还是几个,都执行这一句
                ans = tail.value;
                if(head != tail){
                    tail = tail.last;
                    tail.last = null;
                }else{
                    // 只有一个
                    head = null;
                    tail = null;
                }
                size--;
                return ans;
            }
        }
    
        public static void main(String[] args){
            MyDeque<Integer> deque = new MyDeque<>();
            deque.pushHead(1);
            deque.pollHead();
        }
    }
    

题目

  • K个结点的组内逆序调整。给定一个单链表的头节点head和一个整数k,实现k个结点的小组内部逆序,如果最后一组不够k个就不调整

    调整前:1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 ,k = 3

    调整后:3 → 2 → 1 → 6 → 5 → 4 → 7 → 8

    // 测试链接:https://leetcode.com/problems/reverse-nodes-in-k-group/
    public class Code04_ReverseNodesInKGroup {
    
    	// 不要提交这个类
    	public static class ListNode {
    		public int val;
    		public ListNode next;
    	}
    
    	public static ListNode reverseKGroup(ListNode head, int k) {
    		ListNode start = head;
    		ListNode end = getKGroupEnd(start, k);
    		if (end == null) {
    			return head;
    		}
    		// 第一组凑齐了!
    		head = end;
    		reverse(start, end);
    		// 上一组的结尾节点
    		ListNode lastEnd = start;
    		while (lastEnd.next != null) {
    			start = lastEnd.next;
    			end = getKGroupEnd(start, k);
    			if (end == null) {
    				return head;
    			}
    			reverse(start, end);
    			lastEnd.next = end;
    			lastEnd = start;
    		}
    		return head;
    	}
    
    	public static ListNode getKGroupEnd(ListNode start, int k) {
    		while (--k != 0 && start != null) {
    			start = start.next;
    		}
    		return start;
    	}
    
    	public static void reverse(ListNode start, ListNode end) {
    		end = end.next;
    		ListNode pre = null;
    		ListNode cur = start;
    		ListNode next = null;
    		while (cur != end) {
    			next = cur.next;
    			cur.next = pre;
    			pre = cur;
    			cur = next;
    		}
    		start.next = end;
    	}
    
    }
    
  • 给定两个链表的头节点head1和head2,认为从左到右是某个数字从低位到高位,返回相加之后的链表

    例子:4 → 3 → 6 2 → 5 → 3

    返回:6 → 8 → 9

    解释:634 + 352 = 986

    // 测试链接:https://leetcode.com/problems/add-two-numbers/
    public class Code05_AddTwoNumbers {
    
    	// 不要提交这个类
    	public static class ListNode {
    		public int val;
    		public ListNode next;
    
    		public ListNode(int val) {
    			this.val = val;
    		}
    
    		public ListNode(int val, ListNode next) {
    			this.val = val;
    			this.next = next;
    		}
    	}
    
    	public static ListNode addTwoNumbers(ListNode head1, ListNode head2) {
    		// 长链表和短链表重定向
    		int len1 = listLength(head1);
    		int len2 = listLength(head2);
    		ListNode l = len1 >= len2 ? head1 : head2;
    		ListNode s = l == head1 ? head2 : head1;
    		// 总共三个阶段
    		// 1. 长有短有
    		// 2. 长有短无
    		// 3. 长无短无(判断截止条件:进位为0)
    		ListNode curL = l;
    		ListNode curS = s;
    		ListNode last = curL;
    		int carry = 0;
    		int curNum = 0;
    		// 1. 长有短有
    		while (curS != null) {
    			curNum = curL.val + curS.val + carry;
    			curL.val = (curNum % 10);
    			// 进位变量为carry
    			carry = curNum / 10;
    			last = curL;
    			curL = curL.next;
    			curS = curS.next;
    		}
    		// 2. 长有短无
    		while (curL != null) {
    			curNum = curL.val + carry;
    			curL.val = (curNum % 10);
    			carry = curNum / 10;
    			last = curL;
    			curL = curL.next;
    		}
    		// 3. 长无短无(判断截止条件:进位为0)
    		if (carry != 0) {
    			last.next = new ListNode(1);
    		}
    		return l;
    	}
    
    	// 求链表长度
    	public static int listLength(ListNode head) {
    		int len = 0;
    		while (head != null) {
    			len++;
    			head = head.next;
    		}
    		return len;
    	}
    }
    
  • 给定两个链表的头节点head1和head2,返回合并之后的大链表,要求依然有序

    例子:1 → 3 → 3 → 5 → 7 2 → 2 → 3 → 3 → 7

    返回:1 → 2 → 2 → 3 → 3 → 3 → 3 → 5 → 7

    // 测试链接:https://leetcode.com/problems/merge-two-sorted-lists
    public class Code06_MergeTwoSortedLinkedList {
    
    	// 不要提交这个类
    	public static class ListNode {
    		public int val;
    		public ListNode next;
    	}
    
    	public static ListNode mergeTwoLists(ListNode head1, ListNode head2) {
    		if (head1 == null || head2 == null) {
    			return head1 == null ? head2 : head1;
    		}
    		ListNode head = head1.val <= head2.val ? head1 : head2;
    		ListNode cur1 = head.next;
    		ListNode cur2 = head == head1 ? head2 : head1;
    		ListNode pre = head;
    		while (cur1 != null && cur2 != null) {
    			if (cur1.val <= cur2.val) {
    				pre.next = cur1;
    				cur1 = cur1.next;
    			} else {
    				pre.next = cur2;
    				cur2 = cur2.next;
    			}
    			pre = pre.next;
    		}
    		pre.next = cur1 != null ? cur1 : cur2;
    		return head;
    	}
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值