⭐数组在定义的时候,长度就是固定的,如果想改动数组的长度,就需要重新定义一个新的数组。
⭐链表的长度可以是不固定的,并且可以动态增删, 适合数据量不固定,频繁增删,较少查询的场景。
⭐双指针 + 链表挺有趣,链表的本质也还是考察指针操作,常考的也是这种题型。
视频链接:代码随想录 707.设计链表
题目链接:一道题考完所有基本操作? 707.设计链表
//首先要定义ListNode节点
class ListNode{
int val;
ListNode next;
public ListNode(){
}
public ListNode(int val){
this.val = val;
}
public ListNode(int val,ListNode next){
this.val = val;
this.next = next;
}
}
//我们操作第n个节点时,cur一定是在第n个节点之前,即cur.next才是第n个节点
//这样才能方便操作第n个节点
class MyLinkedList {
//全局属性size和dummyHead
int size;//节点长度
ListNode dummyHead;//虚拟头节点
public MyLinkedList(){
size = 0;
dummyHead = new ListNode(0);
}
//获取1下标为index的节点值
public int get(int index) {
//特判
if(index < 0 || index >= size){
return -1;
}
//cur表示当前节点,用来遍历
ListNode cur = dummyHead;
while(index != 0){
cur = cur.next;
index--;
}
return cur.next.val;
}
public void addAtHead(int val) {
addAtIndex(0,val);
}
public void addAtTail(int val) {
addAtIndex(size,val);
}
//在下标为index的节点 前 ,插入val节点
public void addAtIndex(int index, int val) {
if(index < 0) index = 0;
if(index > size) return;
ListNode cur = dummyHead;
ListNode newNode = new ListNode(val);
while(index != 0){
cur = cur.next;
index--;
}
newNode.next = cur.next;
cur.next = newNode;
size++;
}
//删除下标为index的节点
public void deleteAtIndex(int index) {
//特判
if(index < 0 || index >= size){
return;
}
ListNode cur = dummyHead;
while(index != 0){
cur = cur.next;
index--;
}
cur.next = cur.next.next;
size--;
}
}
⭐设计链表这道题值得多看看,一方面,设计了常规操作,十分基础;另一方面,要学会手写ListNode类。因为力扣的题基本都会给ListNode类,但是机试、面试中,需要手写。
203.移除链表元素
视频链接:代码随想录 203.移除链表元素
题目链接:基础题 203.移除链表元素`
public class ListNode{
int val;
ListNode next;
public ListNode(){
}
public ListNode(int val){
this.val = val;
}
public ListNode(int val,ListNode next){
this.val = val;
this.next = next;
}
}
class Solution {
public ListNode removeElements(ListNode head, int val) {
//虚拟头结点的主要目的是为了避免对头结点的特殊处理。所以可以这样:涉及到对链表修改(如插入,删除,移动)的,都加个dummy,只是遍历取点就可以不用加
ListNode dummyNode = new ListNode(-1,head);//虚拟头节点
ListNode cur = dummyNode;//当前节点。用来遍历
while(cur.next != null){
if(cur.next.val == val){
cur.next = cur.next.next;
}
else cur = cur.next;
}
return dummyNode.next;
}
}
视频链接:代码随想录 206. 反转链表
题目链接:双指针完成 206. 反转链表
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 reverseList(ListNode head) {
ListNode cur = head;//当前节点
ListNode pre = null;//前一个节点
ListNode temp = cur;//临时节点
while(cur != null){
temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}
视频链接:代码随想录 24.两两交换链表中的节点
题目链接:考察基础 24.两两交换链表中的节点
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 swapPairs(ListNode head) {
//首先要明确,如果要对节点4和节点5进行操作,那么cur要在节点3上(提前一个节点)
//其次,因此,cur在一次遍历后,要到下下一个节点(后移2个节点)
ListNode dummyNode = new ListNode(-1,head);
ListNode cur = dummyNode;
//
while(cur.next != null && cur.next.next != null){
ListNode t1 = cur.next;
ListNode t2 = cur.next.next.next;
cur.next = cur.next.next;
cur.next.next = t1;
t1.next = t2;
cur = cur.next.next;
}
return dummyNode.next;
}
}
视频链接:代码随想录 19. 删除链表的倒数第 N 个结点
题目链接:快慢指针 19. 删除链表的倒数第 N 个结点
⭐ 思路:利用快慢指针fast、slow。fas、slow起初都在dummyNode上。
⭐我们知道,链表的一大问题就是操作当前节点必须要找前一个节点才能操作. 删除倒数第n个节点,也就是slow要在倒数n + 1的位置上。
⭐即:终止条件应该是slow在倒数第n + 1的位置,fast在null的位置。
⭐此时,slow与fast相隔n个位置。所以,fast要比slow快n+1步。
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; }
}
思路:利用快慢指针fast、slow。fas、slow起初都在dummyNode上。
我们知道,链表的一大问题就是操作当前节点必须要找前一个节点才能操作.
删除倒数第n个节点,也就是slow要在倒数n + 1的位置上。
即:终止条件应该是slow在倒数第n + 1的位置,fast在null的位置。
此时,slow与fast相隔n个位置。所以,fast要比slow快n+1步。
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummyNode = new ListNode(-1,head);
ListNode fast = dummyNode;
ListNode slow = dummyNode;
//让fast先走n+1步
n += 1;
while(n -- > 0){
fast = fast.next;
}
//fast、slow同时移动,知道fast到null 同时 slow到倒数第n+1位置。
while(fast != null){
fast = fast.next;
slow = slow.next;
}
//删除倒数第n个节点
slow.next = slow.next.next;
return dummyNode.next;
}
}
题目链接:脑筋急转弯? 链表相交
⭐如果有相交的结点D的话,每条链的头结点先走完自己的链表长度,然后回头走另外的一条链表,那么两结点一定为相交于D点,因为这时每个头结点走的距离是一样的,都是AD + BD + DC,而他们每次又都是前进1,所以距离相同,速度又相同,固然一定会在相同的时间走到相同的结点上,即D点
public class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
next = null;
}
}
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode t1 = headA;
ListNode t2 = headB;
while(t1 != t2){
//如果t1为空,那就让t1走headB
if(t1 != null){t1 = t1.next;}
else{t1 = headB;}
//如果t2为空,那就让t2走headA
if(t2 != null){t2 = t2.next;}
else{t2 = headA;}
}
return t1;
}
}
视频链接:代码随想录 142. 环形链表 II
题目链接:数学题?明白a=c,完胜环形链表 II
已知a = c后,设相遇点为l1,l2 = head,长度一样长,那么就同时一直走,l1和l2相遇点,则为入口
class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
next = null;
}
}
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
//为什么是&&,因为如果没有环,对于奇偶个数,退出while条件不同
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
//有环
if(fast == slow){//如果相遇
ListNode l1 = fast;//相遇点为l1(也是l2)
ListNode l2 = head;//l2是头节点
//l1和l2同时走,此时a = c派上用场
while(l1 != l2){
l1 = l1.next;
l2 = l2.next;
}
return l1;//l1=l2,都是入口点
}
}
return null;
}
}
⭐这个图是 代码随想录知识星球 成员:海螺人 所画,总结的非常好,分享给大家。
⭐考察链表的操作其实就是考察指针的操作,是面试中的常见类型。
读研的经历,不仅仅是做好实验认真听课就行,读研的三年,就是要一边又一遍地打磨你的性格,要对权威者绝对服从,要时刻谦卑,处事谨言慎行,要学会圆滑。年少时打出的子弹啊,最终还是正中了眉心。
你可以选择躲在角落里沉默,但是不要嘲笑,甚至诋毁比你勇敢的人。因为他们争取到的光明也会照耀到你!