zoukankan      html  css  js  c++  java
  • 数据结构之链表

     

    1.链表

     链表作为最基本的数据结构,其存储特点如下:可以用任意一组存储单元来存储链表中的数据元素(存储单元可以是不连续的),而且除了存储每个数据元素ai 值以外,还必须存储指示其直接后继元素的信息。

      在Java语言中,可以定义如下的数据类来存储节点信息。

    1 class Node{
    2     Node next = null;
    3     int data;
    4     public Node(int data){
    5         this.data = data;
    6     }
    7 }    

       

    2.链表的操作

      链表最重要的操作就是向链表中插入元素和从链表中删除元素。

           示例给出链表的基本操作:

      1 package wzq.test.algorithm.linkedlist;
      2 
      3 public class MyLinkedList {
      4     static Node head = null;
      5     
      6     public static void main(String[] args) {
      7         MyLinkedList link = new MyLinkedList();
      8         link.addLinkedList(3);
      9         link.addLinkedList(1);
     10         link.addLinkedList(5);
     11         link.addLinkedList(3);
     12         link.addLinkedList(2);
     13         link.print();
     14         System.out.println();
     15         
     16         //设置环
     17 //        Node z =link.findKPost(1);
     18         Node r = link.findKPost(3);
     19 //        if(z.next==null)
     20 //            z.next = r;
     21         //判断两条单向链表是否相交
     22         boolean b = link.isIntersect(link.head, r);
     23         System.out.println(b);
     24         //找到第一次相交的节点
     25         Node f = link.getFirstMeetNode(link.head, r);
     26         System.out.println("相交的节点值是:"+f.data);
     27         //判断链表中是否有环
     28 //        boolean b = link.isLoop(link.head);
     29 //        System.out.println(b);
     30         
     31         //找到环入口
     32 //        Node r0 = link.isLoopNode(link.head);
     33 //        if(r0!=null)
     34 //            System.out.println("环入口值为:"+r0.data);
     35 //        
     36         //删除第index个元素
     37         /*link.deleteLinkedList(1);
     38         link.print();
     39         System.out.println();*/
     40         //找到倒数第k个元素
     41         //Node  k =link.findKPost(3);
     42         //System.out.println(k.data);
     43         //删除重复数据
     44         //link.deleteRepeat();
     45         //输出链表的长度
     46         //System.out.println("length :"+link.getLength());
     47         //从尾到头输出链表
     48         //link.postPrint(link.head);
     49         
     50         //寻找单链表中的中间节点
     51         //Node mid = link.findMidNode();
     52         //System.out.println(mid.data);
     53         //从小到大排序
     54         //link.sortLinkedList();
     55         //link.print();
     56 
     57     }
     58     //如果两个链表相交,找到相交的节点
     59     public Node getFirstMeetNode(Node h1,Node h2){
     60         if(h1==null || h2 == null)
     61             return null;
     62         
     63         int len1 = 1;
     64         Node tail1 = h1;
     65         while(tail1.next!=null){
     66             len1++;
     67             tail1=tail1.next;
     68         }
     69         
     70         
     71         int len2 = 1;
     72         Node tail2 = h2;
     73         while(tail2.next!=null){
     74             len2++;
     75             tail2=tail2.next;
     76         }
     77         
     78         Node t1 = h1;
     79         Node t2 = h2;
     80         if(len1>len2){
     81             int i = len1-len2;
     82             while(i!=0){
     83                 i--;
     84                 t1 = t1.next;
     85             }
     86         }else{
     87             int i = len2-len1;
     88             while(i!=0){
     89                 i--;
     90                 t2 = t2.next;
     91             }
     92         }
     93         while(t1!=null&&t2!=null){
     94             if(t1==t2)
     95                 break;
     96             t1 = t1.next;
     97             t2 = t2.next;
     98         }
     99         
    100         return t1;
    101     }
    102     //判断两个链表是否相交
    103     /*
    104      * 思路:如果两个单向链表相交,则它们的尾节点肯定相同,如果不相同,则说明不相交
    105      */
    106     public boolean isIntersect(Node h1,Node h2){
    107         if(h1==null || h2==null)
    108             return false;
    109         
    110         Node tail1 = h1;
    111         Node tail2 = h2;
    112         
    113         while(tail1.next!=null)
    114             tail1 = tail1.next;
    115         while(tail2.next!=null)
    116             tail2 = tail2.next;
    117         
    118         return tail1 == tail2;
    119     }
    120     
    121     //找到环入口
    122     /*
    123      * 思路:首先通过快慢指针获取到第一次相遇的节点,
    124      *     然后再一个从head开始,一个从相遇的点开始,单步走,必定相遇,相遇第一次的点就是环入口点
    125      */
    126     public Node isLoopNode(Node head){
    127         if(head==null)
    128             return null;
    129         Node fast = head;
    130         Node slow = head;
    131         Node first = null;
    132         while(fast!=null && fast.next!=null){
    133             fast = fast.next.next;
    134             slow = slow.next;
    135             if(fast == slow){
    136                 first = fast;
    137                 break;
    138             }
    139         }
    140         
    141         if(fast==null || fast.next==null || first == null){
    142             return null;
    143         }
    144         Node start = head;
    145         while(start != first){
    146             start = start.next;
    147             first = first.next;
    148         }
    149         
    150         return start;
    151     }
    152     //判断链表中是否有环
    153     /*
    154      * 思路:使用一个快指针和慢指针,同时遍历,如果能得到快指针等于了慢指针,说明有环;
    155      *                                   如果快指针等于了null,说明没有环
    156      */
    157     public  boolean isLoop(Node head) {
    158         if(head==null)
    159             return false;
    160         Node fast = head;//快指针 +2
    161         Node slow = head;//慢指针 +1
    162         while(fast!=null&&fast.next!=null){
    163             
    164             fast=fast.next.next;
    165             slow=slow.next;
    166             
    167             if(fast==slow){
    168                 return true;
    169             }
    170         }
    171         return false;
    172     }
    173 
    174     //寻找单链表的中间节点
    175     /*
    176      * 思路:不用遍历两次得到length求出mid再遍历
    177      *         可以使用两个指针,同时遍历,一个快 走2步,一个慢 走1步
    178      *         当快的走到尾部时,慢的正好在中间
    179      *         (如果链表为偶数,中间两个都算)
    180      */
    181     public Node findMidNode(){
    182         if(head==null){
    183             return null;
    184         }
    185         Node fast = head;//+2
    186         Node slow = head;//+1
    187         while(fast!=null){
    188             if(fast.next==null){
    189                 break;
    190             }
    191             fast = fast.next.next;
    192             slow = slow.next;
    193         }
    194         return slow;
    195     }
    196     //从尾到头输出单链表
    197     /*
    198      * 思路:可以用栈,但是要开辟多余的空间
    199      * 想到栈 就能想到递归,递归的本质就是一个栈结构
    200      * 使用递归输出后面的节点,再输出本节点
    201      */
    202     public void postPrint(Node node){
    203         if(node!=null){
    204             postPrint(node.next);
    205             System.out.print(node.data+" ");
    206         }
    207         
    208     }
    209     
    210     //找到倒数第k个元素
    211     /*
    212      * 思路:指定两个指针,一个p1,一个p2,
    213      * p1需要向前移动k-1个位置
    214      * 然后p1,p2同时遍历链表,只到p1->next指向null,p2正好指向倒数第k个元素
    215      */
    216     public Node findKPost(int k) {
    217         if(k<1)
    218             return null;
    219         Node p1 = head;
    220         Node p2 = head;
    221         for(int i=0 ; i<k-1;i++){
    222             p1 = p1.next;
    223         }
    224         if(p1==null){
    225             System.out.println("k值有问题");
    226             return null;
    227         }
    228         while(p1.next!=null){
    229             p1 = p1.next;
    230             p2 = p2.next;
    231         }
    232         return p2;
    233     }
    234 
    235     //删除重复的数
    236     /*
    237      * 思路:用两个循环,外循环正常遍历链表,内循环从当前cur节点向后遍历,遇到重复的节点就删除。
    238      */
    239     public void deleteRepeat() {
    240         if(head == null)
    241             return ;
    242         Node p = head;
    243         
    244         while(p!=null){
    245             Node q = p;
    246             while(q.next!=null){
    247                 if(p.data==q.next.data){
    248                     q.next = q.next.next;
    249                     //q = q.next;
    250                 }else
    251                     q = q.next;
    252             }
    253             p = p.next;
    254         }
    255     }
    256     //向链表添加
    257     /*
    258      * 思路:从head节点开始遍历,遇到cur->next为null时,添加新节点
    259      */
    260     public void addLinkedList(int d){
    261         Node newNode = new Node(d);
    262         if(head == null){
    263             head = newNode;
    264             return;
    265         }
    266         //Node preNode = null;
    267         Node curNode = head;
    268         while(curNode!=null){
    269             //preNode = curNode;
    270             if(curNode.next==null){
    271                 curNode.next = newNode;
    272                 return;
    273             }
    274             curNode = curNode.next;
    275         }
    276     }
    277     //删除链表指定位置的节点
    278     /*
    279      * 思路:先判断是否为头结点,很容易去掉头结点
    280      *         标量i为2,开始从第二个节点遍历,判断i==index,如果是则删除第i节点
    281      */
    282     public  boolean deleteLinkedList(int index){
    283         if(index==1)
    284         {
    285             head = head.next;
    286             return true;
    287         }
    288         int i =2;
    289         Node preNode = head;
    290         Node curNode = head.next;
    291         while(curNode!=null){
    292             //preNode = curNode;
    293             if(index==i){
    294                 preNode.next = curNode.next;
    295                 return true;
    296             }
    297             i++;
    298             preNode = curNode;
    299             curNode = curNode.next;
    300         }
    301         return false;
    302     }
    303     //打印链表
    304     public  void print(){
    305         if(head==null)
    306             return;
    307         Node curNode = head;
    308         while(curNode!=null){
    309             System.out.print(curNode.data+" ");
    310             curNode = curNode.next;
    311         }
    312     }
    313     //链表排序  从小到大
    314     /*
    315      * 用两个循环,外循环正常遍历链表,内循环从当前cur节点开始与后面的节点值进行比较,
    316      * 如果小于cur节点的值,便进行值得交换
    317      * 然后,再继续遍历下一个节点进行比较
    318      */
    319     public  void sortLinkedList(){
    320         if(head==null){
    321             return;
    322         }
    323         Node curNode = head;
    324         Node preNode;
    325         while(curNode!=null){
    326             preNode = curNode;
    327             while(curNode.next!=null){
    328                 int temp ;
    329                 if(preNode.data > curNode.next.data){
    330                     temp = preNode.data;
    331                     preNode.data = curNode.next.data;
    332                     curNode.next.data = temp;
    333                 }
    334                 curNode = curNode.next;
    335             }
    336             curNode = preNode.next;
    337             
    338         }
    339         
    340     }
    341     //输出链表长度
    342     public  int getLength(){
    343         int length=0;
    344         if(head == null)
    345             return length;
    346         Node curNode = head;
    347         while(curNode!=null){
    348             length++;
    349             curNode = curNode.next;
    350         }
    351         return length;
    352     }
    353     
    354 }
    355 class Node{
    356     int data;
    357     Node next = null;
    358     Node(int data){
    359         this.data = data;
    360     }
    361 }

    1 主要有链表的基本操作,添加和删除结点

     1 //向链表添加
     2     /*
     3      * 思路:从head节点开始遍历,遇到cur->next为null时,添加新节点
     4      */
     5     public void addLinkedList(int d){
     6         Node newNode = new Node(d);
     7         if(head == null){
     8             head = newNode;
     9             return;
    10         }
    11         //Node preNode = null;
    12         Node curNode = head;
    13         while(curNode!=null){
    14             //preNode = curNode;
    15             if(curNode.next==null){
    16                 curNode.next = newNode;
    17                 return;
    18             }
    19             curNode = curNode.next;
    20         }
    21     }
     1 //删除链表指定位置的节点
     2     /*
     3      * 思路:先判断是否为头结点,很容易去掉头结点
     4      *         标量i为2,开始从第二个节点遍历,判断i==index,如果是则删除第i节点
     5      */
     6     public  boolean deleteLinkedList(int index){
     7         if(index==1)
     8         {
     9             head = head.next;
    10             return true;
    11         }
    12         int i =2;
    13         Node preNode = head;
    14         Node curNode = head.next;
    15         while(curNode!=null){
    16             //preNode = curNode;
    17             if(index==i){
    18                 preNode.next = curNode.next;
    19                 return true;
    20             }
    21             i++;
    22             preNode = curNode;
    23             curNode = curNode.next;
    24         }
    25         return false;
    26     }

    2 从链表中删除重复的数据

     1 //删除重复的数
     2     /*
     3      * 思路:用两个循环,外循环正常遍历链表,内循环从当前cur节点向后遍历,遇到重复的节点就删除。
     4      */
     5     public void deleteRepeat() {
     6         if(head == null)
     7             return ;
     8         Node p = head;
     9         
    10         while(p!=null){//外循环
    11             Node q = p;
    12             while(q.next!=null){//内循环
    13                 if(p.data==q.next.data){
    14                     q.next = q.next.next;
    15                     //q = q.next;
    16                 }else
    17                     q = q.next;
    18             }
    19             p = p.next;
    20         }
    21     }

    3 如果找出单链表中的倒数第k个元素

     1 //找到倒数第k个元素
     2     /*
     3      * 思路:指定两个指针,一个p1,一个p2,
     4      * p1需要向前移动k-1个位置
     5      * 然后p1,p2同时遍历链表,只到p1->next指向null,p2正好指向倒数第k个元素
     6      */
     7     public Node findKPost(int k) {
     8         if(k<1)
     9             return null;
    10         Node p1 = head;
    11         Node p2 = head;
    12         for(int i=0 ; i<k-1;i++){
    13             p1 = p1.next;
    14         }
    15         if(p1==null){
    16             System.out.println("k值有问题");
    17             return null;
    18         }
    19         while(p1.next!=null){
    20             p1 = p1.next;
    21             p2 = p2.next;
    22         }
    23         return p2;
    24     }

    4 如何实现链表反转

     1 //反转链表
     2     public void ReverseLink(Node head){
     3         //Node isRevereLink = null;
     4         Node pNode = head;
     5         Node pPrev = null;//倒序链表的头部
     6         while(pNode!=null){
     7             Node pNext = pNode.next;
     8         
     9             pNode.next = pPrev;//从正序链表上截取一个结点 ,前插法放入倒序中
    10             pPrev = pNode;// 指向倒序链表的头部
    11             pNode = pNext;//正序链表中剩余部分
    12         }
    13         this.head = pPrev;
    14     }

    5 如何从尾到头输出链表

     1 //从尾到头输出单链表
     2     /*
     3      * 思路:可以用栈,但是要开辟多余的空间
     4      * 想到栈 就能想到递归,递归的本质就是一个栈结构
     5      * 使用递归输出后面的节点,再输出本节点
     6      */
     7     public void postPrint(Node node){
     8         if(node!=null){
     9             postPrint(node.next);
    10             System.out.print(node.data+" ");
    11         }
    12         
    13     }

    6 如何寻找单链表的中间结点

    //寻找单链表的中间节点
        /*
         * 思路:不用遍历两次得到length求出mid再遍历
         *         可以使用两个指针,同时遍历,一个快 走2步,一个慢 走1步
         *         当快的走到尾部时,慢的正好在中间
         *         (如果链表为偶数,中间两个都算)
         */
        public Node findMidNode(){
            if(head==null){
                return null;
            }
            Node fast = head;//+2
            Node slow = head;//+1
            while(fast!=null){
                if(fast.next==null){
                    break;
                }
                fast = fast.next.next;
                slow = slow.next;
            }
            return slow;
        }

    7 如何检验一个链表是否有环

     1 //判断链表中是否有环
     2     /*
     3      * 思路:使用一个快指针和慢指针,同时遍历,如果能得到快指针等于了慢指针,说明有环;
     4      *                                   如果快指针等于了null,说明没有环
     5      */
     6     public  boolean isLoop(Node head) {
     7         if(head==null)
     8             return false;
     9         Node fast = head;//快指针 +2
    10         Node slow = head;//慢指针 +1
    11         while(fast!=null&&fast.next!=null){
    12             
    13             fast=fast.next.next;
    14             slow=slow.next;
    15             
    16             if(fast==slow){
    17                 return true;
    18             }
    19         }
    20         return false;
    21     }

    找到环入口

    可以参考  https://blog.csdn.net/u011373710/article/details/54024366

     1     //找到环入口
     2     /*
     3      * 思路:首先通过快慢指针获取到第一次相遇的节点,
     4      *     然后再一个从head开始,一个从相遇的点开始,单步走,必定相遇,相遇第一次的点就是环入口点
     5      *  
     6      */
     7     public Node isLoopNode(Node head){
     8         if(head==null)
     9             return null;
    10         Node fast = head;
    11         Node slow = head;
    12         Node first = null;//第一次相遇的结点
    13         //通过快慢指针获取到第一次相遇的节点
    14         while(fast!=null && fast.next!=null){
    15             fast = fast.next.next;
    16             slow = slow.next;
    17             if(fast == slow){
    18                 first = fast;
    19                 break;
    20             }
    21         }
    22         
    23         if(fast==null || fast.next==null || first == null){
    24             return null;
    25         }
    26         //一个从head开始,一个从相遇的点开始,单步走,必定相遇
    27         Node start = head;
    28         while(start != first){
    29             start = start.next;
    30             first = first.next;
    31         }
    32         
    33         return start;
    34     }

    8 如何在不知道头指针的情况下删除指定结点

     分两种情况1.如果是链表尾部结点,无法删除,因为找不到前继结点

         2.将后继结点的值 复制到 要删除的结点值上,然后将后继结点 再链表上删除

     1 public boolean deleteLinkNode(Node n) {
     2         // TODO Auto-generated method stub
     3         if(n==null || n.next == null){
     4             return false;
     5         }
     6         Node nextNode = n.next;
     7         n.data = nextNode.data;
     8         n.next = nextNode.next;
     9         return true;
    10     }

    9 如何判断两个链表是否相交

     1 //判断两个链表是否相交
     2     /*
     3      * 思路:如果两个单向链表相交,则它们的尾节点肯定相同,如果不相同,则说明不相交
     4      */
     5     public boolean isIntersect(Node h1,Node h2){
     6         if(h1==null || h2==null)
     7             return false;
     8         
     9         Node tail1 = h1;
    10         Node tail2 = h2;
    11         
    12         while(tail1.next!=null)
    13             tail1 = tail1.next;
    14         while(tail2.next!=null)
    15             tail2 = tail2.next;
    16         
    17         return tail1 == tail2;
    18     }

    如果两个链表相交,找到相交的节点

     1 //如果两个链表相交,找到相交的节点
     2     public Node getFirstMeetNode(Node h1,Node h2){
     3         if(h1==null || h2 == null)
     4             return null;
     5         //遍历得到链表1的长度
     6         int len1 = 1;
     7         Node tail1 = h1;
     8         while(tail1.next!=null){
     9             len1++;
    10             tail1=tail1.next;
    11         }
    12         
    13         //遍历得到链表2的长度
    14         int len2 = 1;
    15         Node tail2 = h2;
    16         while(tail2.next!=null){
    17             len2++;
    18             tail2=tail2.next;
    19         }
    20         //判断哪个链表更长,长几个结点,然后长的链表先遍历几个结点
    21         Node t1 = h1;
    22         Node t2 = h2;
    23         if(len1>len2){
    24             int i = len1-len2;
    25             while(i!=0){
    26                 i--;
    27                 t1 = t1.next;
    28             }
    29         }else{
    30             int i = len2-len1;
    31             while(i!=0){
    32                 i--;
    33                 t2 = t2.next;
    34             }
    35         }
    36         //同步遍历,直到结点相同为止
    37         while(t1!=null&&t2!=null){
    38             if(t1==t2)
    39                 break;
    40             t1 = t1.next;
    41             t2 = t2.next;
    42         }
    43         
    44         return t1;
    45     }
  • 相关阅读:
    servletContext
    解决Response输出时乱码
    servletConfig
    服务器和浏览器交互过程
    myeclipse配置
    servlet
    http协议
    配置虚拟主机
    配置主页
    开网站步骤
  • 原文地址:https://www.cnblogs.com/weiziqiang/p/8996385.html
Copyright © 2011-2022 走看看