zoukankan      html  css  js  c++  java
  • 链表

    单向链表:

    双向链表:

    单向链表的一些理解:

    通过定义一个节点类Node来表示链表结点,每个结点是这个类的一个对象

    这个类包括一个Node类型的属性next用来保存下一个节点的地址,包括一个属性data用来保存这个节点的数据

    如果这个链表是无环的,则最后一个节点的next为null

    下面是单向链表的实现和一些操作:

    public class MyLink {
    
        Node head;         // 设置一个头部节点
       Node tail;          // 设置一个尾部节点
        
        private static class Node{
            Node next;    // 创建一个Node类型的属性,用来保存后面的结点地址
            // int data;  // 这个表只能存储int型的数据类型
            Object data;  
            public Node(Object data){
                this.data = data;
            }
        }
    
    
        // 向链表尾部或者头部添加节点
        public void addNode(Object d){
            Node newNode = new Node(d);
         // 方法一:添加节点到尾部
            if(head==null){
                head = newNode;
                return;
            }
            Node tmp = head;
            while(tmp.next != null){ 
                tmp = tmp.next;
            }
            tmp.next = newNode;
            /* 方法二:添加节点到头部
         Node newNode = new Node(d);
            newNode.next = head;
            head = newNode;
            */
        }
    
        // 添加节点到指定位置
        public void addNodeByIndex(int index, Object data){
            if(index<0 || index>linkLength()+1)
                System.out.println(">>>超出添加范围,添加失败<<<");
            else {
                Node newNode = new Node(data);
                if (index == 1) {   // 添加到头部
                    newNode.next = head;
                    head = newNode;
                }
                Node tmp = head;
                int i = 1;
                while (true) {     // 添加到任意位置
                    if (i == index-1) {
                        Node target = tmp.next;
                        tmp.next = newNode;
                        newNode.next = target;
                        break;
                    }
                    i++;
                    tmp = tmp.next;
                }
            }
        }
    
        // 删除指定位置的节点
        public void deleteNode(int index){
            if(index<1 || index>linkLength()){
                System.out.println(">>>超出链表范围<<<");
            }
            else {
                if (index == 1) {
                    head = head.next;  // 删除头结点
                }
                int i = 1;
                // 先循环到找到指定节点的前一节点;这种方法可以吗
                Node tmp = head;
                while (tmp.next != null) {  
                    if (i == index - 1)
                        break;
                    tmp = tmp.next;
                    i++;
                }
                Node target = tmp.next;
                tmp.next = target.next;
    //            方法二:这种挺好
    //            int i = 1;
    //            Node preNode = head;
    //            Node curNode = preNode.next;
    //            while (curNode != null) {
    //                if (i == index-1) {
    //                    preNode.next = curNode.next;
    //                    return true;
    //                }
    //                preNode = curNode;
    //                curNode = curNode.next;
    //                i++;
    //            }
            }
    
        }
    
        // 统计链表长度
        public int linkLength(){
            Node tmp = head;
            int len=0;
            // 这里不能用tmp.next!=null做条件,因为当第一个节点为null的时候会报错
            while(tmp!=null){
                tmp = tmp.next;
                len++;
            }
            return len;
        }
    
        // 打印链表数据
        public void printLinkData(){
            System.out.println("---------打印开始------------");
            for(Node x=head; x!=null; x=x.next){
                System.out.println(x.data);
            }
            System.out.println("---------打印结束------------");
            // 打印方法二
    //        Node tmp = head;
    //        while(tmp!=null){
    //            System.out.println(tmp.data);
    //            tmp = tmp.next;
    //        }
        }
    
        // 获取指定位置的链表数据
        public Object getDataByindex(int index){
            if(index<1 || index>linkLength())
                return null;
            Node tmp = head;
            int i=0;
            while(tmp!=null){
                if(i==index-1){
                    return tmp.data;
                }
                i++;
                tmp = tmp.next;
            }
            return null;
        }
    
        public static void main(String[] args){
    //        Node first = new Node(1);
    //        Node second = new Node(2);
    //        Node third = new Node(3);
    //        first.next = second;
    //        second.next = third;
    //        for(Node x=first; x!=null; x=x.next){
    //            System.out.println(x.data);
    //        }
            MyLink myLink = new MyLink();
            myLink.addNode(1);
            myLink.addNode('a');
            myLink.addNode("string");
            myLink.addNode(3.4);
            myLink.addNode(5);
            myLink.printLinkData();
            myLink.deleteNode(3);
            myLink.printLinkData();
            System.out.println("length: " + myLink.linkLength());
            System.out.println("getData: " + myLink.getDataByindex(4));
            myLink.addNodeByIndex(5, "three");
            myLink.printLinkData();
        }
    }
    View Code

    链表反转:

    递归比较难理解,参考:如何递归反转链表

    public Node reverseLink(Node head){
        Node pCurr = head;
        Node pPrev = null;
        while(pCurr!=null){      // 用一个简单的例子来理解过程  first second third
            Node pNext = pCurr.next;
            pCurr.next = pPrev;  // 把null给第一个节点存储,第一个节点给第二个节点储存...  不断反转
            pPrev = pCurr;       
            pCurr = pNext;
        }
        this.head = pPrev;
        return this.head;
    }
    
    // 递归实现
    public Node reverseLinkByRecursion(Node head){
        if(head==null || head.next==null)
            return head;  // 5
        Node newLink = reverseLinkByRecursion(head.next);
        head.next.next = head;   // 1->2->3->4->5   这里实现5->4                          
        head.next = null;                        // 这里实现4—>null
        return newLink;                 // 返回尾结点 5
    }
    
    // 递归实现反转前n个节点
    Node successor = null;
    public Node reverseNLinkByRecursion(Node head, int n){
        if(n==1){                   // 1—>2->3->4->5 反转前3个,需要到3, 3->2->4,3->2->1->4
            successor = head.next;  // 记录n结点的下一个结点,4
            return head;  // 返回新的头结点
        }
        Node newlink = reverseNLinkByRecursion(head.next, n-1);
        head.next.next = head;
        head.next = successor;
        return newlink;
    }
    
    // 实现反转链表m-n之间的结点
    // m和n同速走,先到达m的位置,再反转后面的结点,例如反转2-4之间的结点 m==1的时候head=2, n=3(2,3,4)
    public Node reverseMN(Node head, int m, int n){
        if(m==1){
            return reverseNLinkByRecursion(head, n);
        }
        head.next = reverseMN(head.next, m-1, n-1);
        return head;
    }

     

    链表排序

    // 链表排序(O(n^2)的时间复杂度)
    public void orderList(Node head){
        Node tmp = head;
        while(tmp!=null){
            Node pNext = tmp.next;
            while(pNext!=null){
                if(tmp.data>pNext.data){
                    int temp = pNext.data;
                    pNext.data = tmp.data;
                    tmp.data = temp;
                }
                pNext = pNext.next;
            }
            tmp = tmp.next;
        }
    }
    不要用选择排序,用归并

    O(nlogn)时间复杂度的链表排序归并排序+找链表的中间结点+合并两条有序链表  

    O(1)空间的链表插入排序:插入排序

    链表的快速排序

    // 方法三:快速排序
    public ListNode quickSort(ListNode head, ListNode end){
        if(head==end || head.next==end) return head;
        ListNode lhead = head, utail = head, p = head.next;
        while (p != end){
            ListNode next = p.next;
            if(p.val < head.val){ // 如果p的值小于head的值,头插
                p.next = lhead;
                lhead = p;   // 维护小于head部分的头指针
            }
            else { // 如果p的值大于等于head的值,尾插
                utail.next = p;
                utail = p;   // 维护大于等于head部分的尾指针
            }
            p = next;
        }
        utail.next = end;
        ListNode node = quickSort(lhead, head);  // 小于等于head的部分
        head.next =  quickSort(head.next, end);  // 大于等于head的部分
        return node;
    }
    链表快排

    快慢指针查找中间节点:

    快指针的速度比慢指针的快一倍,当快指针走到尾部的时候,慢指针正好走到中间

    public void findMiddleData(Node head){
        // 快慢指针,慢指针每次走一格,快指针每次走两格
        Node p = this.head, q = this.head;
        while(p!=null && p.next!=null){
            p = p.next.next;
            q = q.next;
        }
        System.out.println("Middle data: " + q.data);
    }

    快慢指针查找链表是否有环,并且找到环的入口:

    为什么快慢指针一定会相遇

    // 链表指定位置成环
    // 要增加一个Node类型的成员变量记录尾结点,通过尾结点指向其他节点来成环
    
    // 快慢指针查找链表是否有环
    // 快指针的速度可以不是 2 而是其他的 3,4 之类的吗?  比较麻烦
    public boolean findLoop(Node head){
        Node fast = head, slow = head;
        while(fast!=null && fast.next!=null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow) {return true;
            }
        }return false;
    }
    
    // 快慢指针找环的入口,相遇后慢指针再走a,起始点指针再走a,就是环的入口
    public Node findLoopPort(Node head){
        Node fast = head, slow = head;
        while(fast!=null && fast.next!=null) {
            fast = fast.next.next;
            slow = slow.next;
            if(fast==slow)
                break;
        }
        slow = head;
        while(slow != fast){
            slow = slow.next;
            fast = fast.next; 
        }
        return slow;
    }

    查找倒数第K个元素(双指针):

    用两个指针a、b,a先走K步,然后a,b同时同速走,a和b之间的差距为K,a到了尾部之后,b就到了倒数第K个元素;

      删除倒数第K个元素leetcode

    删除排序链表中的重复元素

    合并K路有序链表

    从尾到头打印单链表(递归):

    public void printLinkReversed(Node node){
        if(node!=null){
            // node = node.next; // 不能用这种,区别就是最后一个null会被下面的node.data所使用
            // printLinkReversed(node);
            printLinkReversed(node.next);
            System.out.println(node.data);
        }
    }
    View Code

    学习来源:

    链表

    链表的原理及java实现

  • 相关阅读:
    摘录的WinForm Control 开发3,crystalbutton
    摘录的WinForm Control 开发2,CustomColorCheckBox,CustomColorRadioButton
    摘录的WinForm Control 开发4,控件总结
    linq to sql 简单小结:
    摘录的WinForm Control 开发5,WinForm 开发:FlashGetFormDemos ,类似FlashGet的悬浮窗
    SerialPort 的使用
    摘录的WinForm Control 开发5,WinForm 开发:MultipLanguageDemos ,多国语言开发
    摘录的WinForm Control 开发5,WinForm 开发:UnRegularFormDemos ,不规则窗体
    摘录的WinForm control 开发1..BorderLabel
    jquery上传插件(uploadify)的使用
  • 原文地址:https://www.cnblogs.com/pineapple-chicken/p/13907582.html
Copyright © 2011-2022 走看看