链表的逆置
给定一个单链表,实现链表的逆置功能
思路:
在前面的单链表练习中,讲到了头插,头插的链表和正常的序列是刚好相反的,利用这点来进行链表的逆置
代码讲解:
使用cur指针指向第一个数据域,把head单独拿出来,重新对head头插一次,在头插中,cur的next域要先把head的next域接过来,然后把head的next置为cur,这样做的话会丢失cur后面的所有未插入节点,就要使用一个指针next来进行记录,然后cur从next中拿值,不断的更新next。
private void reverse() {
Entry cur = head.next;
Entry next = null;
//开始插入第一个,先把head的next置为空
head.next = null;
while (cur != null) {
//更新next,next用来保存cur后面的未插入的指针
next = cur.next;
//头插
cur.next = head.next;
head.next = cur;
//更新cur
cur = next;
}
}
合并两个有序的链表
给定两个有序的链表,写一个函数实现合并两个链表的功能,而且合并后的链表是有序的。
思路:
使用归并的思想,两个指针分别指向两个链表,哪个指针所指的节点数据域小,那么就把该节点连接到合并后的序列上
代码讲解:
首先明确该方法是链表类的一个方法,所以第一条链表就是本身的head,第二条链表是传进来的list2,分别用两个指针指向单链表,last指向head意思是把list2链表合并到list1上面,所以合并后的序列是last,然后在p和q都没有到最后的情况下,哪个小就接入到last后面,跳出循环后,哪个链表有剩余直接插入到last后面。
这里的两个链表都有4,但是我比较的条件是
所以会更改路径,先连接q的4,这块可以写成<=
代码实现:
public void merge(MiNiLinkedListTest list2) {
//用p,q分别引用两个单链表
Entry p = head.next;
Entry q = list2.getHead().next;
//使用last指针来进行归并这两个指针
Entry last = head;
last.next = null;
//两个链表都有节点
while(p!=null && q!=null) {
//谁小接到last的后面
if(p.data > q.data){
last.next = q;
q = q.next;
last = last.next;
} else {
last.next = p;
p = p.next;
last = last.next;
}
}
//list1还有剩余节点
if(p != null) {
last.next = p;
}
//list2还有剩余节点
if(q != null) {
last.next = q;
}
//已经把list2全部加到了list1上面,list2直接把head的next置为空
list2.getHead().next = null;
}
判断链表是否有环
给定一个链表,判断链表是否有环
思路:
采用快慢指针进行判断,道理很简单,圆形操场跑步,跑的快的人总会追上跑得慢的人,也就是套圈。
给一个有环的例子:
这里从67到78就构成了一个环,来构造一下这个链表,正常的插入肯定是不会出现这种情况的,所以链表的插入就需要我们自己来构造,要自己来设定下一个节点就需要打开private,可以在外部修改链表
这里直接给出构造的代码,方便读者进行测试
MiNiLinkedListTest list1 = new MiNiLinkedListTest();
Entry head = list1.getHead();
Entry node1 = new Entry(34,null);
head.next = node1;
Entry node2 = new Entry(21,null);
node1.next = node2;
Entry node3 = new Entry(67,null);
node2.next = node3;
Entry node4 = new Entry(42,null);
node3.next = node4;
Entry node5 = new Entry(12,null);
node4.next = node5;
Entry node6 = new Entry(78,null);
node5.next = node6;
node6.next = node3;
代码讲解:
代码的实现是比较简单的,定义快指针和慢指针同时指向第一个数据节点,然后在不为空的情况下,两个指针开始走,一个走两步,一个走一步,如果再次相遇,则证明链表是有环的,如果有其中一个为空,那么就说明链表是可以走完的,那么就没有环。
private boolean isLoop() {
//定义快慢指针
Entry fast = head.next;
Entry slow = head.next;
while(fast != null && slow != null && fast.next != null) {
//快指针每次走两步,慢指针每次走一步
fast = fast.next.next;
slow = slow.next;
if(fast == slow) {
//如果再次相遇,则说明有环,返回true
return true;
}
}
return false;
}
求链表环的入口节点
是上一个问题的延伸,当链表有环时返回链表的入口节点
还是用上一个题的例子来说,我们首先来做一个分析,搞清楚如何去求链表的入口节点。
如图所示,为这几段距离定义了一写变量
x:第一个数据节点到环入口的距离
m:环入口到相遇之间的距离
n:相遇节点到环末尾之间的距离
规律:由于fast每次走2步,slow每次走1步,所以fast的路程是slow路程的2倍
通过上面一起来进行一系列的推导:
fast走过的路程是:x+m+n+m
slow走过的路程是:x+m
故可得:x+m+n+m = 2 * (x+m)
x+n+2m = 2x+2m
x = n
所以最终得出来的结论是x=n
所以我们只需要定义一个指针从第一个数据开始,一个指针从相遇的节点开始,两个节点同时开始,每次同时走一步,相遇的时候就是环的入口节点。
代码实现:
private Integer getLoopHead() {
//定义快慢指针
Entry fast = head.next;
Entry slow = head.next;
while(fast != null && slow != null && fast.next != null) {
//快指针每次走两步,慢指针每次走一步
fast = fast.next.next;
slow = slow.next;
if(fast == slow) {
//如果再次相遇,则说明有环
//定义一个指针指向第一个数据节点
Entry cur = head.next;
//cur和slow(fast)同时走,相遇时就是入口节点
while(true) {
cur = cur.next;
slow = slow.next;
if(cur == slow) {
return cur.data;
}
}
}
}
return null;
}
判断两条链表是否相交
给定两条链表,判断两条链表是否相交
先来通过一张图来直观的看一下链表的相交
可以看到 list1 有5个数据节点, list2 有4个数据节点,如何找到相交的那个节点或者说如何判断是否相交呢?
思路:
首先先分别统计两个链表的长度,求出两个链表的差值,然后再重新开始遍历,让长的链表的指针先走两个链表的长度差值,然后两个指针开始同时走,每走一步判断一次是否是同一个节点
具体到这个图中,定义两个指针,分别指向 list1 和 list2,让 list1 先走1步,然后 list1 和 list2 同时开始走,每走一步判断一次,走两步后到6这两个指针就会相交。
为了大家的测试方便,这里先给出构造相交链表的代码:
MiNiLinkedListTest list1 = new MiNiLinkedListTest();
MiNiLinkedListTest list2 = new MiNiLinkedListTest();
Entry node1 = new Entry(1, null);
Entry node2 = new Entry(2, null);
Entry node3 = new Entry(3, null);
Entry node4 = new Entry(4, null);
Entry node5 = new Entry(5, null);
Entry node6 = new Entry(6, null);
Entry node7 = new Entry(7, null);
list1.head = node1;
node1.next = node2;
node2.next = node3;
node3.next = node6;
node6.next = node7;
list2.head = node4;
node4.next = node5;
node5.next = node6;
代码实现:
public boolean isCross(MiNiLinkedListTest list2) {
//统计两个链表的长度
int size1 = 0;
int size2 = 0;
//统计list1
Entry cur = head;
while(cur != null) {
cur = cur.next;
size1++;
}
//统计list2
cur = list2.head;
while(cur != null) {
cur = cur.next;
size2++;
}
//为两条链表定义指针
Entry p = head.next;
Entry q = list2.head.next;
//判断哪条链表长
if(size1 > size2) {
int cnt = size1 - size2;
while (cnt-- > 0) {
p = p.next;
}
} else if(size1 < size2) {
int cnt = size2 - size1;
while (cnt-- > 0) {
q = q.next;
}
}
//开始同时遍历
while(p != null && q != null) {
if(p == q) {
return true;
}
p = p.next;
q = q.next;
}
return false;
}
找链表的倒数第k个节点
思路:
如果只定义一个指针也是可以的,但是很明显需要遍历两遍,第一遍找到链表的总长度,第二遍从头开始走总长度-k个位置即可。
如果只遍历一遍,那么可以定义两个指针,如图,开始时,先让lastK指针指向第一个节点,last指针走正数k个位置,然后两个指针同时开始走,当last走到末尾的时候,lastK就是倒数第k个节点。
代码实现:
public int lastOrder(int k) {
//定义两个指针
Entry lastK = head.next;
Entry last = head;
//last先走k个位置
while(k-- > 0) {
last = last.next;
if(last == null) {
throw new IllegalArgumentException("超过范围的 k :"+ k);
}
}
//两个指针同时走
while(last.next != null) {
last = last.next;
lastK = lastK.next;
}
return lastK.data;
}
链表实现大数的加法
这种算是链表的一种应用,其实实现起来比较的简单,只需要把每一位数字用链表存储起来,然后加起来,注意进位即可。
需要注意的有两点:
- 使用头插,这样可以把数字逆过来,这样个位在前面,比较好处理
- 注意进位的问题, 这里使用了一个标志位来进行记录是否需要进位,每次相加都不断的进行更新
代码实现
public static void main(String[] args) {
String num1 = "36578768657546342546560870899434";
String num2 = "254354657658768768543532424";
//先用链表把这两个大数存起来
LinkedList<Integer> list1 = new LinkedList<>();
LinkedList<Integer> list2 = new LinkedList<>();
for (int i = 0; i < num1.length(); i++) {
list1.addFirst(num1.charAt(i)-'0');
}
for (int i = 0; i < num2.length(); i++) {
list2.addFirst(num2.charAt(i)-'0');
}
//定义一个是否需要进位的标志
boolean bool = false;
//定义结果集合
LinkedList<Integer> result = new LinkedList<>();
Iterator<Integer> iterator1 = list1.iterator();
Iterator<Integer> iterator2 = list2.iterator();
while(iterator1.hasNext() && iterator2.hasNext()) {
Integer p = iterator1.next();
Integer q = iterator2.next();
int temp = p + q;
if(bool) {
temp += 1;
bool = false;
}
if(temp > 9) {
temp = temp%10;
bool = true;
}
result.addFirst(temp);
}
//长的数字还没处理完
while (iterator1.hasNext()) {
Integer temp = iterator1.next();
if(bool) {
temp += 1;
bool = false;
}
if(temp > 9) {
temp = temp%10;
bool = true;
}
result.addFirst(temp);
}
while (iterator2.hasNext()) {
Integer temp = iterator2.next();
if(bool) {
temp += 1;
bool = false;
}
if(temp > 9) {
temp = temp%10;
bool = true;
}
result.addFirst(temp);
}
System.out.println(result);
}