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

    在面试过程中,数据结构和算法基本上算是研发类岗位必考的部分,而链表基本上又是数据结构中相对容易掌握、而且容易出题的部分,因此我们先整理一下链表部分的经典题目。

    (声明:以下所有程序都是用java编写)

    首先,我们来定义一个链表的数据结构,如下:

     
     1 public class Link {
     2     private int value;
     3     private Link next;
     4     public void set_Value(int m_Value) {
     5         this.value = m_Value;
     6     }
     7     public int get_Value() {
     8         return value;
     9     }
    10     public void set_Next(Link m_Next) {
    11         this.next = m_Next;
    12     }
    13     public Link get_Next() {
    14         return next;
    15     }
    16 }
     

    有了这个数据结构后,我们需要一个方法来生成和输出链表,其中链表中每个元素的值采用的是随机数。

    生成链表的代码如下:

     
     1     public static Link init(int count, int maxValue)
     2     {
     3         Link list = new Link();
     4         Link temp = list;
     5         Random r = new Random();
     6         temp.set_Value(Integer.MIN_VALUE);
     7         for (int i = 0; i < count; i++)
     8         {
     9             Link node = new Link();
    10             node.set_Value(r.nextInt(maxValue));
    11             temp.set_Next(node);
    12             temp=node;
    13         }
    14         temp.set_Next(null);
    15         return list;
    16     }
    17     
    18     public static Link init(int count)
    19     {
    20         return init(count, Integer.MAX_VALUE);
    21     }
     

    对于链表的头结点,我们是不存储任何信息的,因此将其值设置为Integer.MIN_VALUE。我们重载了生成链表的方法。

    下面是打印链表信息的方法:

     
     1     public static void printList(Link list)
     2     {
     3         if (list == null || list.get_Next() == null)
     4         {
     5             System.out.println("The list is null or empty.");
     6             return;
     7         }
     8         Link temp = list.get_Next();
     9         StringBuffer sb = new StringBuffer();
    10         while(temp != null)
    11         {
    12             sb.append(temp.get_Value() + "->");
    13             temp=temp.get_Next();
    14         }
    15         System.out.println(sb.substring(0, sb.length() - 2));
    16     }
     

    好了,有了以上这些基础的方法, 我们就可以深入探讨链表相关的面试题了。

    • 链表反转 思路:有两种方法可以实现链表反转,第一种是直接循环每个元素,修改它的Next属性;另一种是采取递归的方式。 首先来看直接循环的方式:
       
       1     public static Link Reverve(Link list)
       2     {
       3         if (list == null || list.get_Next() == null || list.get_Next().get_Next() == null)
       4         {
       5             System.out.println("list is null or just contains 1 element, so do not need to reverve.");
       6             return list;
       7         }        
       8         Link current = list.get_Next();
       9         Link next = current.get_Next();
      10         current.set_Next(null);
      11         while(next != null)
      12         {
      13             Link temp = next.get_Next();
      14             next.set_Next(current);
      15             current = next;
      16             next = temp;
      17         }
      18         list.set_Next(current);
      19         
      20         return list;
      21     }
       

      然后是递归方式:

       
       1     public static Link RecursiveReverse(Link list)
       2     {
       3         if (list == null || list.get_Next() == null || list.get_Next().get_Next() == null)
       4         {
       5             System.out.println("list is null or just contains 1 element, so do not need to reverve.");
       6             return list;
       7         }
       8         
       9         list.set_Next(Recursive(list.get_Next()));
      10         
      11         return list;
      12     }
      13     
      14     
      15     private static Link Recursive(Link list)
      16     {
      17         if (list.get_Next() == null)
      18         {
      19             return list;
      20         }
      21         Link temp = Recursive(list.get_Next());
      22         list.get_Next().set_Next(list);
      23         list.set_Next(null);
      24         
      25         return temp;
      26 
       
    • 输出指定位置的元素(倒数第N个元素) 思路:采用两个游标来遍历链表,第1个游标先走N步,然后两个游标同时前进,当第一个游标到最后时,第二个游标就是想要的元素。
       
       1     public static Link find(Link list, int rPos)
       2     {
       3         if (list == null || list.get_Next() == null)
       4         {
       5             return null;
       6         }
       7         int i = 1;
       8         Link first = list.get_Next();
       9         Link second = list.get_Next();
      10         while(true)
      11         {
      12             if (i==rPos || first == null) break;
      13             first = first.get_Next();
      14             i++;
      15         }
      16         if (first == null)
      17         {
      18             System.out.println("The length of list is less than " + rPos + ".");
      19             return null;
      20         }
      21         while(first.get_Next() != null)
      22         {
      23             first = first.get_Next();
      24             second = second.get_Next();
      25         }
      26         
      27         return second;
      28     }
       
    • 删除指定节点 思路:可以分情况讨论,如果指定节点不是尾节点,那么可以采用取巧的方式,将指定节点的值修改为下一个节点的值,将指定节点的Next属性设置为Next.Next;但如果指定节点为尾节点,那么只能是从头开始遍历。
       
       1     public static void delete(Link list, Link element)
       2     {
       3         if (element.get_Next() != null)
       4         {
       5             element.set_Value(element.get_Next().get_Value());
       6             element.set_Next(element.get_Next().get_Next());
       7         }
       8         else
       9         {
      10             Link current = list.get_Next();
      11             while(current.get_Next() != element)
      12             {
      13                 current = current.get_Next();
      14             }
      15             current.set_Next(null);
      16         }
      17     }
       
    • 删除重复节点 思路:采用hashtable来存取链表中的元素,遍历链表,当指定节点的元素在hashtable中已经存在,那么删除该节点。
       
       1     public static void removeDuplicate(Link list)
       2     {
       3         if (list == null || list.get_Next() == null || list.get_Next().get_Next() == null) return;
       4         Hashtable table = new Hashtable();
       5         Link cur = list.get_Next();
       6         Link next = cur.get_Next();
       7         table.put(cur.get_Value(), 1);
       8         while(next != null)
       9         {
      10             if (table.containsKey(next.get_Value()))
      11             {
      12                 cur.set_Next(next.get_Next());
      13                 next = next.get_Next();                 
      14             }
      15             else
      16             {
      17                 table.put(next.get_Value(), 1);
      18                 cur= next;
      19                 next = next.get_Next();
      20             }
      21             
      22         }        
      23     }
       
    • 寻找链表中间节点 思路:采用两个游标的方式,第一个游标每次前进两步,第二个游标每次前进一步,当第一个游标到最后时,第二个游标就是中间位置。需要注意的是,如果链表元素的个数是偶数,那么中间元素应该是两个。
       
       1     public static void findMiddleElement(Link list)
       2     {
       3         if (list == null || list.get_Next() == null) return;
       4         System.out.println("The Middle element is:");
       5         if (list.get_Next().get_Next() == null)
       6         {
       7             System.out.println(list.get_Next().get_Value());
       8         }
       9         Link fast = list.get_Next();
      10         Link slow = list.get_Next();
      11         while(fast.get_Next() != null && fast.get_Next().get_Next() != null)
      12         {
      13             fast = fast.get_Next().get_Next();
      14             slow = slow.get_Next();
      15         }
      16             
      17         if (fast != null && fast.get_Next() == null)
      18         {
      19             System.out.println(slow.get_Value());
      20         }
      21         else
      22         {
      23             System.out.println(slow.get_Value());
      24             System.out.println(slow.get_Next().get_Value());
      25         }
      26     }
       
    • 链表元素排序 思路:链表元素排序,有两种方式,一种是链表元素本身的排序,一种是链表元素值得排序。第二种方式更简单、灵活一些。
       
       1     public static void Sort(Link list)
       2     {
       3         if (list == null || list.get_Next() == null || list.get_Next().get_Next() == null)
       4         {
       5             return;
       6         }
       7         Link current = list.get_Next();
       8         Link next = current.get_Next();
       9         while(current.get_Next() != null)
      10         {
      11             while(next != null)
      12             {
      13                 if (current.get_Value() > next.get_Value())
      14                 {
      15                     int temp = current.get_Value();
      16                     current.set_Value(next.get_Value());
      17                     next.set_Value(temp);
      18                 }
      19                 next = next.get_Next();
      20             }
      21             current = current.get_Next();
      22             next = current.get_Next();
      23         }
      24     }
       
    • 判断链表是否有环,如果有,找出环上的第一个节点 思路:可以采用两个游标的方式判断链表是否有环,一个游标跑得快,一个游标跑得慢。当跑得快的游标追上跑得慢的游标时,说明有环;当跑得快的游标跑到尾节点时,说明无环。 至于如何找出换上第一个节点,可以分两步,首先确定环上的某个节点,计算头结点到该节点的距离以及该节点在环上循环一次的距离,然后建立两个游标,分别指向头结点和环上的节点,并将距离平摊(哪个距离大,先移动哪个游标,直至两个距离相等),最后同时移动两个游标,碰到的第一个相同元素,就是环中的第一个节点。
       
       1     public static Link getLoopStartNode(Link list)
       2     {
       3         if (list == null || list.get_Next() == null || list.get_Next().get_Next() == null)
       4         {
       5             return null;
       6         }
       7         int m = 1, n = 1;
       8         Link fast = list.get_Next();
       9         Link slow = list.get_Next();
      10         while(fast != null && fast.get_Next() != null)
      11         {
      12             fast = fast.get_Next().get_Next();
      13             slow = slow.get_Next();
      14             if (fast == slow) break;
      15             m++;
      16         }
      17         if (fast != slow)
      18         {
      19             return null;
      20         }
      21         Link temp = fast;
      22         while(temp.get_Next() != fast)
      23         {
      24             temp = temp.get_Next();
      25             n++;
      26         }
      27         Link node1 = list.get_Next();
      28         Link node2 = fast;
      29         if (m < n)
      30         {
      31             for (int i = 0; i < n - m; i++)
      32             {
      33                 node2 = node2.get_Next();
      34             }
      35         }
      36         if (m > n)
      37         {
      38             for (int i = 0; i < m - n; i++)
      39             {
      40                 node1 = node1.get_Next();
      41             }
      42         }
      43         while(true)
      44         {
      45             if (node1 == node2)
      46             {
      47                 break;
      48             }
      49             node1 = node1.get_Next();
      50             node2 = node2.get_Next();
      51         }
      52         
      53         return node1;
      54         
      55     }
       
    • 判断两个链表是否相交 思路:判断两个链表的尾节点是否相同,如果相同,一定相交
       
       1     public static boolean isJoint(Link list1, Link list2)
       2     {
       3         if (list1 == null || list2 == null || list1.get_Next() == null || list2.get_Next() == null)
       4         {
       5             return false;
       6         }
       7         Link node1 = list1;
       8         Link node2 = list2;
       9         while(node1.get_Next() != null)
      10         {
      11             node1 = node1.get_Next();
      12         }
      13         while(node2.get_Next() != null)
      14         {
      15             node2 = node2.get_Next();
      16         }
      17         
      18         return node1 == node2;
      19     }
       
    • 合并两个有序链表 思路:新建一个链表,然后同时遍历两个有序链表,比较其大小,将元素较小的链表向前移动,直至某一个链表元素为空。然后将非空链表上的所有元素追加到新建链表中。
       
       1     public static Link merge(Link list1, Link list2)
       2     {
       3         Link list = new Link();
       4         list.set_Value(Integer.MIN_VALUE);
       5         Link current1 = list1.get_Next();
       6         Link current2 = list2.get_Next();
       7         Link current = list;
       8         while(current1 != null && current2 != null)
       9         {
      10             Link temp = new Link();
      11             if (current1.get_Value() > current2.get_Value())
      12             {
      13                 temp.set_Value(current2.get_Value());
      14                 current2 = current2.get_Next();
      15             }
      16             else
      17             {
      18                 temp.set_Value(current1.get_Value());
      19                 current1 = current1.get_Next();
      20             }
      21             current.set_Next(temp);
      22             current = temp;
      23         }
      24         if (current1 != null)
      25         {
      26             while(current1 != null)
      27             {
      28                 Link temp = new Link();
      29                 temp.set_Value(current1.get_Value());
      30                 current.set_Next(temp);
      31                 current = temp;
      32                 current1 = current1.get_Next();
      33             }
      34         }
      35         
      36         if (current2 != null)
      37         {
      38             while(current2 != null)
      39             {
      40                 Link temp = new Link();
      41                 temp.set_Value(current2.get_Value());
      42                 current.set_Next(temp);
      43                 current = temp;
      44                 current2 = current2.get_Next();
      45             }
      46         }
      47         
      48         current.set_Next(null);
      49         
      50         return list;
      51     }
       
    • 交换链表中任意两个元素(非头结点) 思路:首先需要保存两个元素的pre节点和next节点,然后分别对pre节点和next节点的Next属性重新赋值。需要注意的是,当两个元素师相邻元素时,需要特殊处理,否则会将链表陷入死循环。
       
       1     public static void swap(Link list, Link element1, Link element2)
       2     {
       3         if (list == null || list.get_Next() == null || list.get_Next().get_Next() == null || 
       4                 element1 == null || element2 == null || element1 == element2) 
       5             return;
       6                 
       7         Link pre1 = null, pre2 = null, next1 = null, next2 = null;
       8         Link cur1=element1, cur2=element2;
       9         Link temp = list.get_Next();
      10         boolean bFound1 = false;
      11         boolean bFound2 = false;
      12         while(temp != null)
      13         {
      14             if(temp.get_Next() == cur1)
      15             {
      16                 pre1=temp;
      17                 next1 = temp.get_Next().get_Next();
      18                 bFound1 = true;
      19             }
      20             if (temp.get_Next() == cur2)
      21             {
      22                 pre2 = temp;
      23                 next2 = temp.get_Next().get_Next();
      24                 bFound2=true;
      25             }
      26             if (bFound1 && bFound2) break;
      27             temp = temp.get_Next();
      28         }
      29         
      30         if (cur1.get_Next() == cur2)
      31         {
      32             temp = cur2.get_Next();
      33             pre1.set_Next(cur2);
      34             cur2.set_Next(cur1);
      35             cur1.set_Next(temp);
      36         }
      37         else if (cur2.get_Next() == cur1)
      38         {
      39             temp = cur1.get_Next();
      40             pre2.set_Next(cur1);
      41             cur1.set_Next(cur2);
      42             cur2.set_Next(temp);
      43         }
      44         else
      45         {
      46             pre1.set_Next(cur2);
      47             cur1.set_Next(next2);
      48             pre2.set_Next(cur1);
      49             cur2.set_Next(next1);
      50         }
      51     }
       

      这里,还有另外一种取巧的方法,就是直接交换两个元素的值,而不需要修改引用。

       
       1     public static void swapValue(Link list, Link element1, Link element2)
       2     {
       3         if (element1 == null || element2 == null)
       4         {
       5             return;
       6         }
       7         int temp = element1.get_Value();
       8         element1.set_Value(element2.get_Value());
       9         element2.set_Value(temp);
      10     }
       

      不过,这种方式,应该不是面试官所希望看到的。

  • 相关阅读:
    Java自学
    java自学
    每日总结
    每日总结
    每日总结
    每周总结
    每日总结
    每日总结
    每日总结
    每日总结
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/3444649.html
Copyright © 2011-2022 走看看