6-4 链表拼接 (20分)_数据结构之链表

本文详细介绍了链表的相关操作,包括链表的定义、生成、输出,以及链表反转、输出指定位置元素、删除指定节点、删除重复节点、寻找中间节点、排序、判断环并找到环起点、判断链表相交、合并有序链表和交换链表元素等面试常考问题的解决方案。通过示例代码和思路解析,帮助读者深入理解链表操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

6ebb79bdbf9fac3de5371e12c7d6c856.png

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

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

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

View Code 

 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 }

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

生成链表的代码如下:

View Code 

 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。我们重载了生成链表的方法。

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

View Code 

 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属性;另一种是采取递归的方式。
    首先来看直接循环的方式:
View Code 

 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     }

然后是递归方式:

View Code 

 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步,然后两个游标同时前进,当第一个游标到最后时,第二个游标就是想要的元素。

View Code 

 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;但如果指定节点为尾节点,那么只能是从头开始遍历。

View Code 

 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中已经存在,那么删除该节点。

View Code 

 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     }

寻找链表中间节点

思路:采用两个游标的方式,第一个游标每次前进两步,第二个游标每次前进一步,当第一个游标到最后时,第二个游标就是中间位置。需要注意的是,如果链表元素的个数是偶数,那么中间元素应该是两个。

View Code 

 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     }

链表元素排序

思路:链表元素排序,有两种方式,一种是链表元素本身的排序,一种是链表元素值得排序。第二种方式更简单、灵活一些。

View Code 

 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     }

判断链表是否有环,如果有,找出环上的第一个节点

思路:可以采用两个游标的方式判断链表是否有环,一个游标跑得快,一个游标跑得慢。当跑得快的游标追上跑得慢的游标时,说明有环;当跑得快的游标跑到尾节点时,说明无环。

至于如何找出换上第一个节点,可以分两步,首先确定环上的某个节点,计算头结点到该节点的距离以及该节点在环上循环一次的距离,然后建立两个游标,分别指向头结点和环上的节点,并将距离平摊(哪个距离大,先移动哪个游标,直至两个距离相等),最后同时移动两个游标,碰到的第一个相同元素,就是环中的第一个节点。

View Code 

 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     }

判断两个链表是否相交

思路:判断两个链表的尾节点是否相同,如果相同,一定相交

View Code 

 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     }

合并两个有序链表

思路:新建一个链表,然后同时遍历两个有序链表,比较其大小,将元素较小的链表向前移动,直至某一个链表元素为空。然后将非空链表上的所有元素追加到新建链表中。

View Code 

 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属性重新赋值。需要注意的是,当两个元素师相邻元素时,需要特殊处理,否则会将链表陷入死循环。

View Code 

 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     }

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

View Code 

 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     }

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

学习资料分享交流群:1093776732 入群有全套学习视频资料电子书免费赠送!

参考资料:

什么是链表及原理_数据结构之链表精讲_嵌入式开发工程师​www.makeru.com.cn
7677fc4a9cffb2c5377a4a633507342a.png
链表的反转_数据结构之链表精讲_嵌入式开发工程师​www.makeru.com.cn
7677fc4a9cffb2c5377a4a633507342a.png
单链表实现-链表删除_数据结构之链表精讲_嵌入式开发工程师​www.makeru.com.cn
7677fc4a9cffb2c5377a4a633507342a.png
循环链表及线性表的应用_循环链表及线性表的应用_嵌入式开发工程师​www.makeru.com.cn
7677fc4a9cffb2c5377a4a633507342a.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值