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

 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. 【高级功能】使用Web存储

    Web存储允许我们在浏览器里保存简单的键/值数据.Web存储和cookie很相似,但它有着更好的实现方式,能保存的数据量也很大.这两种类型共享相同的机制,但是被保存数据的可见性和寿命存在区别. PS: ...

  2. SharePoint 2013 日历重叠功能简介

    之前做了一个“SharePoint 2013 日历根据Category显示不同颜色”,然后大家留言说为什么不用SharePoint自带的日历重叠功能,所以自己尝试一下这个功能,和之前的博客进行一下对比 ...

  3. SharePoint:WebPartPageUserException This page has encountered a critical error

    遇到如下webpart莫名错误,很常见吧.一般用户是直接删掉,知道原因的不算太多. 解决办法(Solution): Usually, This error caused by wrong entrie ...

  4. Android 从Gallery获取图片

    本文主要介绍Android中从Gallery获取图片 设计项目布局 <LinearLayout xmlns:android="http://schemas.android.com/ap ...

  5. AFNetworking 3.0 源码解读 总结

    终于写完了 AFNetworking 的源码解读.这一过程耗时数天.当我回过头又重头到尾的读了一篇,又有所收获.不禁让我想起了当初上学时的种种情景.我们应该对知识进行反复的记忆和理解.下边是我总结的 ...

  6. 你真的了解UIScrollView吗?

    一:首先查看一下关于UIScrollView的定义 NS_CLASS_AVAILABLE_IOS(2_0) @interface UIScrollView : UIView <NSCoding& ...

  7. Android 6.0 运行时权限处理

    在运行时请求权限 从Android 6.0(API级别23)开始,用户权限授予应用程序在应用程序运行时,当他们安装程序.这种方法简化了应用程序的安装过程,因为用户不需要安装或更新应用程序时授予权限.这 ...

  8. Learn how to Use UIPageViewController in iOS

    下面学习内容来自国外的IOS学习网站:The AppGuruz: UIPageViewController in iOS  也许需要FQ哦 认真做一遍上面入门UIPageController的教程,然 ...

  9. sqlserver 附加数据库失败,错误提示:5拒绝访问 解决办法

    sqlserver 附加数据库失败,错误提示:5拒绝访问 解决办法 金刚 sqlserver 附加数据库 拒绝访问 今天把项目拷贝到新硬盘里,发现在附加数据库中提示:操作系统错误5:"5拒绝 ...

  10. 基于ruby的watir自动化测试 笔记一

    基于Ruby的watir-webdriver自动化测试方案与实施(五)   基于Ruby的watir-webdriver自动化测试方案与实施(四)   基于Ruby的watir-webdriver自动 ...