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

 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     }

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

Java中的链表数据结构的更多相关文章

  1. Java基础-JAVA中常见的数据结构介绍

    Java基础-JAVA中常见的数据结构介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是数据结构 答:数据结构是指数据存储的组织方式.大致上分为线性表.栈(Stack) ...

  2. 【Java】Java中的Collections类——Java中升级版的数据结构【转】

    一般来说课本上的数据结构包括数组.单链表.堆栈.树.图.我这里所指的数据结构,是一个怎么表示一个对象的问题,有时候,单单一个变量声明不堪大用,比如int,String,double甚至一维数组.二维数 ...

  3. java中的各个数据结构区别

    ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要设计到数组元素移动等内存操作,所以索引数据快插入数据慢 ...

  4. Java中HashMap的数据结构

    类声明: 概述: 线程不安全: <Key, Value>两者都可以为null: 不保证映射的顺序,特别是它不保证该顺序恒久不变: HashMap使用Iterator: HashMap中ha ...

  5. Java中常见的数据结构的区别

    把多个数据按照一定的存储方式,存储起来,称存储方式之为数据结构. 数据的存储方式有很多,数组,队列,链表,栈,哈希表等等. 不同的数据结构,性能是不一样的,比如有的插入比较快,查询比较快,但是删除比较 ...

  6. Java 中常见的数据结构

    1.数据结构有什么作用? 当使用 Java 里面的容器类时,你有没有想过,怎么 ArrayList 就像一个无限扩充的数组,也好像链表之类的.很好使用,这就是数据结构的用处,只不过你在不知不觉中使用了 ...

  7. java中常用的数据结构--Collection接口及其子类

    java中有几种常用的数据结构,主要分为Collection和map两个主要接口(接口只提供方法,并不提供实现),而程序中最终使用的数据结构是继承自这些接口的数据结构类. 一.集合和数组的区别 二.C ...

  8. Java中常用的数据结构类

    结构体系图 List ArrayList.LinkedList.Vector有什么区别? ArrayList 只能装入引用对象(基本类型要转换为封装类): 线程不安全: 底层由数组实现(顺序表),因为 ...

  9. java中实现链表(转)

    分析: 上述节点具备如下特征: 1. 每个节点由两部分组成(存储信息的字段,存储指向下一个节点的指针) 2. 节点之间有着严格的先后顺序. 3. 单链表节点是一种非线性的结构,在内存中不连续分配空间. ...

随机推荐

  1. 轻松玩转jquery。

    一.简介 jQuery创始人是美国John Resig,是优秀的Javascript框架: jQuery是一个轻量级.快速简洁的javaScript库.源码戳这 jQuery产生的对象时jQuery独 ...

  2. Sass的使用和基础语法

    sass安装 官网下载ruby的windows安装包,安装时勾选上添加到环境变量add ruby executables to your path.安装完成后打开命令行,ruby -v输出内容则安装完 ...

  3. HTML标签的嵌套

    随着时间的推移,我们学习html的基础知识有了大概的了解.而我发现,平时在写html文档的时候,发现不太清楚标签之间的嵌套规则,经常是想到什么标签就用那些,然而发现有时的标签嵌套却是错误的.通过网上找 ...

  4. [Android]AndroidInject增加sqlite3数据库映射注解(ORM)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/3623050.html AndroidInject项目是我写的一 ...

  5. iOS开发200个tips总结(一)

    tip 1 :  给UIImage添加毛玻璃效果 func blurImage(value:NSNumber) -> UIImage { let context = CIContext(opti ...

  6. 【代码笔记】iOS-对iphone手机进行判断的一些函数

    代码: #import "RootViewController.h" //为判断手机的型号 -(NSString*)deviceString添加头文件 #import " ...

  7. iOS 学习 - 13.微信分享链接、QQ 分享图片

    准备工作---原文来自这个 首先要在微信开放平台申请 AppID 和 QQ ID(我第一天晚上申请的,第二天中午就通过了),接着导入 SDK,也就是3个 .h 和一个 .a 文件,详情见这里 如果你是 ...

  8. IT软件开发常用英语词汇

    Aabstract 抽象的abstract base class (ABC)抽象基类abstract class 抽象类abstraction 抽象.抽象物.抽象性access 存取.访问access ...

  9. Apache Project SVN Download Sit

    apache project svn download sit : http://svn.apache.org/repos/asf 如果想要研究相关项目的源码的话.或者想要成为某个项目的开发者, 就可 ...

  10. Rac grid用户启停监听报错无权限

    [grid@max1 ~]$ lsnrctl stop LSNRCTL for Linux: Version 11.2.0.3.0 - Production on 04-NOV-2016 00:20: ...