博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
数据结构之链表
阅读量:6361 次
发布时间:2019-06-23

本文共 13546 字,大约阅读时间需要 45 分钟。

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

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;

  输出指定位置的元素(倒数第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             else16             {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         else22         {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             else17             {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         else45         {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     }

  

转载于:https://www.cnblogs.com/lizeyang/p/5549746.html

你可能感兴趣的文章
Java数据结构----栈(Stack)源码分析和个人简单实现
查看>>
codis集群完整搭建过程详解
查看>>
LVS介绍以及部署
查看>>
Centos6 安装cdh5.7
查看>>
Outlook 2010添加Exchange Online用户
查看>>
VSS6.0 admin密码清除
查看>>
git status遇到old mode问题
查看>>
字符,字节和编码 一
查看>>
强制关闭数据库
查看>>
502错误详解
查看>>
ARM开发板 嵌入式Linux 修改开机启动LOGO
查看>>
Baby-gin
查看>>
Docker实践(2)—虚拟网络
查看>>
C#实现多线程的方法:线程(Thread类)和线程池(ThreadPool)
查看>>
Ubuntu安装pintos
查看>>
看这里,教你如何快速将pdf文件翻译成中文
查看>>
开源Linux监控系统:Icinga
查看>>
Android模拟器检测常用方法
查看>>
sqlite 中判断某个表是否存在的方法
查看>>
历史数据的清理方法
查看>>