美文网首页数据结构
2018-11-08 单向链表的一系列操作

2018-11-08 单向链表的一系列操作

作者: 秋林格瓦斯 | 来源:发表于2018-11-09 10:25 被阅读0次

    我们在这篇文章主要记录下链表的基本操作. 主要包括

    • 反转链表
    • 检测环
    • 两个有序的链表合并
    • 删除倒数第K个节点
    • 求链表的中间结点

    首先定义单向链表, 数据包含整型的变量和下一个节点的指针.

     public static class Node {
            private int data;
            private Node next;
    
            public Node(int data, Node next) {
                this.data = data;
                this.next = next;
            }
    
            public int getData() {
                return data;
            }
        }
    

    定义print方法,顺序打印链表的值.验证结果的正确性.

    public static void print(Node list) {
            while (list != null) {
                System.out.print(list.data);
                list = list.next;
            }
    
            System.out.println("======================");
        }
    

    一. 反转链表

    链表前进的过程中记录下前一个节点的指针. 同时改变当前节点的指针. 使当前节点指向前一个节点.最后返回反转后的链表. 我练习的时候,犯了一个错误. 我想用同一个变量表示反转前, 反转后的链表. 但是这样是行不通的. 因为这里面list传递过来的是一个引用. 而我再list.next = prev; 这一行代码把list的内在属性改变了. 相当于list.next =null; list没有下一个节点了. list 中只有头结点的数据. 这里需要注意.

     /**
         * 反转链表
         *
         * @param list 入参链表
         * @return 反转链表
         */
        public static Node reverse(Node list) {
            Node prev = null;
            Node current = list;
            Node head = null;
            while (current != null) {
                Node next = current.next;
                current.next = prev;
                prev = current;
                if (next == null) {
                    head = current;
                }
                current = next;
            }
            return head;
    
        }
    
     @Test
        public void reverseTest() {
            Node head = new Node(1, null);
            head.next = new Node(2, null);
            print(head);
            Node rNode = reverse(head);
            System.out.println("++++反转链表+++");
            print(rNode);
        }
    

    二 . 检测环

    使用快慢指针操作. 快指针一次走两步. 慢指针一次走一步. 如果存在环. 那么就会存在扣圈的情况. 终将相遇.
    这里注意处理边界条件即可

       /**
         * 检测链表中是否存在环
         *
         * @param head
         * @return
         */
        private boolean checkCircle(Node head) {
            if (head == null) {
                return false;
            }
    
            Node fast = head;
            Node slow = head;
    
            while (fast != null && fast.next != null) {
                fast = fast.next.next;
                slow = slow.next;
                if (fast == slow) {
                    return true;
                }
            }
            return false;
        }
    
     @Test
        public void checkCircleTest() {
            Node one = new Node(1, null);
            Node two = new Node(2, null);
            Node three = new Node(3, null);
            Node four = new Node(4, null);
            one.next = two;
            two.next = three;
            three.next = four;
            four.next = two;
    
            boolean hasCircle = checkCircle(one);
            System.out.println(hasCircle);
        }
    

    三. 两个有序的链表合并

    面试有碰到过问两个有序数组合并的情况. 和这种应该类似. 就是首先比较两个链表中第一个元素的值. 把较小节点的作为新链表的头结点.移动节点到下一个位置. 然后在循环体中比较. 较小的放到新链表中. 并移动到下一个节点的位置. 最后考虑一种情况. 有可能一个链表已经移动到尾节点. 这时候把另外一个链表剩余的那部分节点.添加到新链表. 并返回新链表.

    **
         * 合并两个有序链表
         *
         * @param list1 链表1
         * @param list2 链表2
         * @return 合并后的链表
         */
        private Node merge(Node list1, Node list2) {
            Node head;
    
            if (list1 == null) {
                return list2;
            }
            if (list2 == null) {
                return list1;
            }
    
            if (list1.data < list2.data) {
                head = list1;
                list1 = list1.next;
            } else {
                head = list2;
                list2 = list2.next;
            }
    
            Node current = head;
            while (list1 != null && list2 != null) {
                if (list1.data < list2.data) {
                    current.next = list1;
                    list1 = list1.next;
                } else {
                    current.next = list2;
                    list2 = list2.next;
                }
                current = current.next;
    
            }
            if (list1 != null) {
                current.next = list1;
            } else {
                current.next = list2;
            }
    
            return head;
        }
    
    @Test
        public void mergeTest() {
            Node one = new Node(1, null);
            Node two = new Node(2, null);
            Node three = new Node(3, null);
            Node four = new Node(4, null);
            Node five = new Node(5, null);
    
            one.next = three;
            three.next = five;
            two.next = four;
    
            Node list1 = one;
            Node list2 = two;
    
            print(list1);
            print(list2);
            Node mergeList = merge(list1, list2);
            print(mergeList);
        }
    

    四. 删除倒数第K个节点

    我先跑,如果你能追上我. 我就跟你嘿嘿嘿. 就是这么骚气. 我怎么知道倒数第K个节点. 这里也是用两个指针. fast 指针先跑K个单位. slow这时候开始跑. fast跑到结尾的时候. slow此时的位置恰好就是倒数第K个位置.
    这就把问题转化成小学数学题了.

    /**
         * 删除倒数第k个数据
         *
         * @param list
         * @param k
         */
        private Node deleteLastKTest(Node list, int k) {
            int i = 1;
            Node slow = list;
            Node fast = list;
            while (fast != null && i < k) {
                fast = fast.next;
                i++;
            }
    
            if (fast == null) {
                return list;
            }
    
            Node pre = null;
            while (fast.next != null) {
                fast = fast.next;
                pre = slow;
                slow = slow.next;
            }
    
    
            if (pre == null) {
                list = list.next;
            } else {
                pre.next = pre.next.next;
            }
            return list;
    
    
        }
    
    @Test
        public void deleteLastKTest() {
            Node one = new Node(1, null);
            Node two = new Node(2, null);
            Node three = new Node(3, null);
            Node four = new Node(4, null);
            Node five = new Node(5, null);
    
            one.next = two;
            two.next = three;
            three.next = four;
            four.next = five;
    
            int k = 5;
            Node node = deleteLastKTest(one, k);
            print(node);
        }
    

    五. 求链表的中间结点

    快慢指针. 一个二倍速fast. 一个一倍速slow. fast 走到结尾. slow恰好走一半.

     /**
         *  返回中间节点
         * @param list
         * @return
         */
        private Node middleNode(Node list) {
            if(list==null){
                return null;
            }
            Node fast = list;
            Node slow = list;
            while(fast.next!=null && fast.next.next!=null){
                fast = fast.next.next;
                slow = slow.next;
            }
    
            return slow;
        }
    
     @Test
        public void middleNodeTest() {
            Node one = new Node(1, null);
            Node two = new Node(2, null);
            Node three = new Node(3, null);
            Node four = new Node(4, null);
            Node five = new Node(5, null);
            Node six = new Node(6,null);
    
            one.next = two;
            two.next = three;
            three.next = four;
            four.next = five;
            five.next = six;
    
            Node node = middleNode(one);
             System.out.println(node.data);
        }
    

    相关文章

      网友评论

        本文标题:2018-11-08 单向链表的一系列操作

        本文链接:https://www.haomeiwen.com/subject/ldlgxqtx.html