链表在笔试面试胸还是很重要的,为了加深印象,我们今天整几道反转链表得经典题目,废话不多说,直接开整
目录
1.反转链表
反转链表可以说是比较经典的题目了,而且代码和思维都比较简单
这里我们采用最朴素的方法,直接在原链表的头节点处进行头插,就可以对链表进行反转了
注意:这里要注意头插完之后要对原头节点的next域置空,否则的话节点将形成环
我们用这样的四个节点的链表来举例
我们要对这个链表进行反转,也就是得到如下的链表
这样我们就可以考虑使用头插法,把2先头插到1的前面,再把3插到2的前面
我们进行如下的步骤
通过这张图,就可以理解到头插是如何进行的了,接下来就是通过代码得到实现了
public ListNode ReverseList(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode cur = head.next;
head.next = null;
while(cur != null){
ListNode curNext = cur.next;
cur.next = head;
head = cur;
cur = curNext;
}
return head;
}
如上就是代码的实现,我们可以看到,链表的反转这一块用了四行代码,我们根据图来走,可以很轻松的理解。我举个例子
就由第一次头插来说
就这样一步一步的走,从而达到反转链表的效果
时间复杂度:O(n)
空间复杂度:O(1)
这样我们就做掉了一道简单的链表题,那我们趁热打铁,再做一题稍微进阶一点的
2.链表内指定区间反转
这一题属于是上面一题的进阶玩法,其实并没有难多少,但是牛客却给这道题提了一个难度
我们直接看着题的描述和示例
这里我们可以发现这题不用我们处理m,n异常的情况,所以就省去了一些麻烦
看到这题,你有没有想到思路呢?
是不是依然是通过头插法,但是这次我们截取一段进行头插即可
就好像这个链表
让我们对2位置开始4位置结束进行反转 ,我拿到这题,思路直接就是先截取再反转,最后再拼接
也就是我们先把2-4先抽取出来,用两个节点先记住1节点和5节点,然后对234进行反转,最后再把1和5拼接上去即可,因为反转链表上面已经讲过,这里就不在赘述,直接看代码,我们通过代码来讲解
public ListNode reverseBetween (ListNode head, int m, int n) {
if(head.next == null){
return head;
}
ListNode mPrev = head; //左边链表的最后一个节点
ListNode nHead = head; //右边链表的第一个节点
if(m == 1){
while(n >0){
nHead = nHead.next; //找到右边节点的第一个节点
n--;
}
ListNode cur = head.next; //反转中间链表的临时变量
ListNode tmp = head; //记录右边第一个节点的前一个节点
while(cur != nHead){ //反转链表
ListNode curNext = cur.next;
cur.next = head;
head = cur;
cur = curNext;
}
tmp.next = nHead; //因为m == 1 所以不用拼接左边的节点
}else{
while(m-2 > 0){
mPrev = mPrev.next; //找到左边节点的最后一个节点
m--;
}
while(n >0){
nHead = nHead.next; //同上
n--;
}
ListNode mHead = mPrev.next;//中间链表的头节点
ListNode cur = mHead.next;
ListNode tmp = mHead;
while(cur != nHead){
ListNode curNext = cur.next;
cur.next = mHead;
mHead = cur;
cur = curNext;
}
tmp.next = nHead; //拼接右边链表
mPrev.next = mHead; //拼接左边链表
}
return head;
}
这里我们考虑了m = 1 和 m != 1的两种情况,因为这两种情况操作不同,所以用稍有不同的代码来实现,如果你有更好,更简洁的代码,欢迎指正
节点如图所示,只要通过代码一点一点实现,即可理解本题
到这里我们再上升一个难度,体验一下较难题,我们真的无从下手吗?
3.链表的回文结构
这题我们可以看到,在牛客网已经达到了惊人的较难级别了,但是真的有那么难吗?
回文我们都知道是什么,那我们单链表是无法从后往前遍历的,那我们怎么判断回文呢?
对,就是链表的反转,我们只需要在中间位置,对中间位置后面的元素进行反转,然后再一次比较,就可以得到结果了
如图所示
我们只要将上面的变成下面的,然后一个从左边节点开始遍历,一个从右边节点开始遍历就可以达到想要得到结果
这里我们如果像让一个节点走到最后,一个节点走到中间,我们只需要利用快慢“指针”即可
快慢“指针”:
设置一个fast,一个slow,都从第一个开始走,fast一次走两步,slow一次走一步,这样我们的fast的速度就是slow的两倍,当fast走完的时候,slow刚好走到中间位置,利用代码实现就是这样的
ListNode fast = head;
ListNode slow = head;
//找到中间节点
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
然后我们得到了最后一个和中间的节点之后,再进行反转
//对中间之后的节点头插
ListNode cur = slow.next;
while(cur != null){
ListNode curNext = cur.next;
cur.next = slow;
slow = cur;
cur = curNext;
}
反转之后,再用slow往后走,一个从头节点开始走,就可以达到判断回文的效果
注意:这里的slow经过上面的反转代码之后,已经走到了最后那个1
ListNode right = head;
while(head != slow && head.next != slow){
if(head.val != slow.val){
return false;
}
slow = slow.next;
head = head.next;
}
return true;
这样我们再处理一下细节上面的问题,就可以通过编译了
全部代码如下:
public boolean chkPalindrome(ListNode head) {
if(head == null){
return true;
}
ListNode fast = head;
ListNode slow = head;
//找到中间节点
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
//对中间之后的节点头插
ListNode cur = slow.next;
while(cur != null){
ListNode curNext = cur.next;
cur.next = slow;
slow = cur;
cur = curNext;
}
ListNode right = head;
while(head != slow && head.next != slow){
if(head.val != slow.val){
return false;
}
slow = slow.next;
head = head.next;
}
return true;
这样我们就六枪打掉了三道和反转链表有关的题目,每天刷点题,让自己更强大