#算法#leetcode#链表
今天练习链表相关的题目,本身链表在平时工作中直接使用的不是很多,至少我是这样,但是实际Java集合中大量用到了链表来进行对应集合的实现,比如HashMap。当然很多需要增删的场景,链表一定是最佳选择。
链表
什么是链表,链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。
我直观的感受,就是一条由相近数据元素连接起来的链条,但是不同于数组,链表每个元素的存储不是连续的,因为链表有next引用,可以通过这个引用索引到下个元素。有不够严谨的地方请大家一定指点。
203. 移除链表元素
题目链接:203. 移除链表元素 - 力扣(LeetCode)
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
这个就是基础的链表操作,遍历,找到target节点,然后删除链表元素。代码:
public ListNode removeElements(ListNode head, int val) {
if(null == head) {
return null;
}
ListNode cur = new ListNode();
cur.next = head;
ListNode pre = cur;
while(null != pre.next) {
if(pre.next.val == val) {
pre.next = pre.next.next; //delete the target node
} else {
pre = pre.next; //move
}
}
return cur.next;
}
创建cur节点,其next指向当前链表,个人感觉这样易于操作和理解,pre作为指针节点,遍历链表,值相同则删除,不同则移动节点。最终返回就用cur的next即可。
707. 设计链表
题目链接:707. 设计链表 - 力扣(LeetCode),题目太长,可以打开链接查看。
这道题在我理解就是封装我们自己的链表,暴露一些常用的操作链表的方法,也就是增、删、改、查。题目本身不难,跟以前通过数组实现Java中的Arraylist类似,只是如果真的作为工具类对外提供,要有严谨的思考,边界、长度、以及线程安全等。我这里也完成了代码,能跑通过,但是提交提示超时,应该是某个查找有异常,先放上第一次完成的代码,提交正常后更新。看到文章的朋友也可以先肉眼排查下问题。
class MyLinkedList {
class ListNode{
int val;
ListNode next;
ListNode() {}
ListNode(int val) {this.val = val;}
}
ListNode head;
int size;
public MyLinkedList() {
head = new ListNode();
size = 0;
}
public int get(int index) {
ListNode point = head;
if(index < 0 || null == point.next) {
return -1;
}
int i = 0;
while(i <= index) {
point = point.next;
i++;
}
return point.val;
}
public void addAtHead(int val) {
ListNode node = new ListNode(val);
if(null == head.next) {
head.next = node;
} else {
node.next = head.next;
head.next = head;
}
size++;
}
public void addAtTail(int val) {
ListNode node = new ListNode(val);
ListNode pointer = head;
while(null != pointer.next) {
pointer = pointer.next;
}
pointer.next = node;
size++;
}
public void addAtIndex(int index, int val) {
if(index > size) {
return;
}
ListNode node = new ListNode(val);
ListNode pointer = head;
int i = 0;
while(i < index) {
pointer = pointer.next;
i++;
}
node.next = pointer.next;
pointer.next = node;
size++;
}
public void deleteAtIndex(int index) {
if(index < 0 || index > size) {
return;
}
ListNode pointer = head;
int i = 0;
while(i < index) {
pointer = pointer.next;
i++;
}
pointer.next = pointer.next.next;
size--;
}
}
206.反转链表
经典!这道题刷过leetcode的应该都知道吧,以前也是写的很熟练,但是重新开始,也是需要理解才行,昨天想了下,知道双指针的思路,但是因为太晚,没写完,今天睡了一觉感觉思路清晰,一口气就写完了,中间也提交了两次。
第一次是判断条件不对,我们循环的条件应该是前面的指针节点不为空,我写成了null != pre.next,这样其实遍历到最后一个节点时,pre.next 就是空的,导致结果不对。
第二次是返回的不对,最终遍历完成,pre节点应该指向了空节点,我们返回前一个cur节点即可。
解决方法:两个指针,pre指向前一个节点,cur指向后一个节点,创建temp节点记录断链后原链表的头结点,然后断链,再反转pre和cur,pre指向cur,直到遍历完链表。这个建议网上找个示意的视频,会直观一点,我是手动画的,就不做视频了。具体代码:
public ListNode reverseList(ListNode head) {
if(null == head) {
return null;
}
ListNode cur = null;
ListNode pre = head;
ListNode temp = null; //锻炼时记录第一个节点
while(null != pre) {
temp = pre.next; //记录
pre.next = cur; //断链,并反转
cur = pre;
pre = temp; //移动指针
}
return cur;
}
今天练习了三道题目,都是很经典的链表相关题目,继续加油!