24.两两交换链表
刚开始做的时候没有定义虚拟头结点,写得比较乱。
- 定义一个dummy,让它指向head。
- 用临时变量存两个值:1.dummy下一个结点的地址值。2.两两交换部分的下一个结点的地址值。
- 思考:为什么需要这两个临时变量?
- 交换结点的意义在于:交换node1和node2的next域里的地址值。如图中node1的next里是100,node2里的是150。
- 第一步。dummy里的next改成100(这个100是node2的地址,表示dummy下一个结点变成node2)
- 第二步。我们现在已经把node2结点换到dummy的下一个了!接下来需要让node1结点变成node2的下一个结点,怎么做呢,就是把node2的next域里的地址修改成node1结点的地址(这里就需要用到我们刚刚保存的临时变量了,图中tmp1=50,50就是node1结点的地址值),那么我们修改node2.next=50,到此,node1就变成node2的下一个结点了。
- 第三步。到这里还没结束,我们完成了结点的交换,还需要把链表给连起来对吧,所以node1.next我们也需要改变,改成原来node2的next。但是刚刚node2.next已经被我们改成50了!所以这个时候临时变量的用处又来了,还好我们保存了一份,所以node1.next=tmp2。链表又被重新的连接起来了。
- 第四步。让cur指针移动到node1的位置。继续上述操作!
19.删除倒数第n个结点
这里要考虑几个边界情况!一开始我没有考虑到。
- 倒数第n个节点是头结点
- 倒数第n个节点是尾结点
判断逻辑:
if (n == ListLen) {
head=head.next;
dummy.next=head;
}else if(n==1){
cur.next=null;
}else {
//删除操作
.......
}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
if(head==null){
return null;
}
if(n==1){
}
ListNode p=head;
int ListLen=0;
ListNode dummy=new ListNode(0);
dummy.next=head;
ListNode cur=dummy;
//记录链表长度
while(p!=null){
p=p.next;
ListLen++;
}
for(int i=0;i<ListLen-n;i++){
cur=cur.next;
}
//判断倒数第n个结点是否是头尾结点
if (n == ListLen) {
head=head.next;
dummy.next=head;
}else if(n==1){
cur.next=null;
}else {
cur.next = cur.next.next;
}
return dummy.next;
}
}
160.相交链表
我们知道链表的每一个结点在内存中都是有一个独立地址的。
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/e7ef04a07e054ff090adff773144bd83.png像上图,节点1和节点3的next域里面存储的地址相等,假设是100,也就是说它们会一同指向这个空间里地址为100的节点,也就是2(假设它的地址值是100)。这个结点在这个空间里肯定是唯一的,2会指向下一个节点。。。后面都是公共的了
我们只需要用两个指针分别遍历两个链表,只要当这两个指针指向了同一个地址值(换句话说就是两个指针的值相等)我们就可以说找到了相交的第一个节点。
上面这个说法只适用于长度一样的链表,因为长度一样,步子都是一样的,走到相交点的步子肯定一样。
但如果是长度不一样的链表,我们就要计算他们的差值,加入说A表比B表长2,那么我们为了让他们保持同一步子走到相交节点,我们要让A先走两步。(因为假如他们相交了,那相交之后的长度是一样的,也就是说A和B不相交的部分,A比B长2,A走完这两步,就可以和B同一起点开始走向他们相交的点)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA==null||headB==null){
return null;
}
//计算两个链表的长度
ListNode curA1=headA;
ListNode curB1=headB;
ListNode curA2=headA;
ListNode curB2=headB;
//记录两个链表的长度
int listlenA=0;
int listlenB=0;
while(curA1!=null){
curA1=curA1.next;
listlenA++;
}
while(curB1!=null){
curB1=curB1.next;
listlenB++;
}
int diff=0;//记录两个链表的长度差
int flag=0;//0表示A长,1表示B长
if(listlenA>listlenB){
diff=listlenA-listlenB;
}
if(listlenB>listlenA){
diff=listlenB-listlenA;
flag=1;//B更长
}
//如果两个链表没有长度差,可以从头开始遍历
if(diff==0){
while(curB2!=null&&curA2!=null){
if(curA2!=curB2){
//指针往后移动
curA2=curA2.next;
curB2=curB2.next;
}else{
return curA2;
}
}
//两个链表存在长度差,需要让长的那个链表先走几步,走到和短的链表同一位置
}else{
//如果链表A更长,就链表A先走
if(flag==0){
while(diff!=0){
curA2=curA2.next;
diff--;
}
//如果链表B更长,就链表B先走
}else{
while(diff!=0){
curB2=curB2.next;
diff--;
}
}
//两个链表从同一起点开始遍历
while(curB2!=null&&curA2!=null){
if(curA2!=curB2){
curA2=curA2.next;
curB2=curB2.next;
}else{
return curA2;
}
}
}
return null;
}
}
142.环形链表
自己写没什么思路。看了解答。
假设头节点到环入口的距离是m,入口到相遇点的距离是y,相遇点走回路口的距离是x,环的长度是n
快指针的速度是慢指针的两倍
一定是快指针先进环里绕圈等慢指针。慢指针第一次进环的时候它们就会相遇
设快指针绕了k圈.
快指针走过的路程=m+kn+y
慢指针走过的路程=m+y
m+kn+y=2(m+y)
化简得
m=kn-y=kn-(n-x)=(k-1)n+x
(k-1)n表示运动了n圈,(k-1)n+x表示绕了(k-1)圈后又走了x
所以定义两个指针,一个在head,一个放在相遇点,同步出发
等到他们相遇的时候,就是环的入口。
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {// 有环
ListNode index1 = fast;
ListNode index2 = head;
// 两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口
while (index1 != index2) {
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}
return null;
}
}