代码随想录算法训练营第三天|203.移除链表元素、707.设计链表、206.反转链表
203.移除链表元素
问题简述:移除链表值为val的元素。
思考:看起来不难,实际上还是容易出错。leecode链表的题head本身就是内容有效的第一个节点,所以在移除时可以考虑在head前添加虚拟头节点dummy,同时还要考虑循环条件不要出错。
算法思路:当head为空时,直接范围head;当head不为空时,创建一个虚拟头节点,依次遍历每个节点,当数字为val时,删除该节点。
时间复杂度 :O(n)
空间复杂度 :O(1)
/**
* 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 removeElements(ListNode head, int val) {
if (head == null) {
return head;
}
//创建虚拟头节点
ListNode dummy = new ListNode(-1, head);
ListNode cur = dummy;
//当cur的下一个结点不为空时,判断下一个结点的值是否为val
while (cur.next != null){
if(cur.next.val == val){
cur.next = cur.next.next;
}else {
cur = cur.next;
}
}
return dummy.next;
}
}
707.设计链表
问题简述:设计一个MyLinkList类,该类为一个链表。并实现初始化链表、获取index索引元素值、在头插入结点、尾插入结点、在index索引前添加元素、删除index索引元素。
思考:这道题的索引很容易弄错,题目将第一个有效元素作为索引0,第二个元素索引1。我们在引入虚拟头节点后,第二个结点才是索引0。同时每一个方法的都需要更新链表长度,链表长度为有效元素个数,而不是结点个数。自己在写的时候就是因为忘了更新链表长度所以改了很长时间。
class MyLinkedList {
int length;
//head为虚拟头节点
ListNode head;
//初始化链表
public MyLinkedList() {
length = 0;
head = new ListNode(0);
}
//得到索引为index的结点值
public int get(int index) {
//判断index是否符合要求
if(index < 0 || index > length - 1){
return -1;
}
ListNode cur = head;
//一共循环index+1次后,cur正好指向索引为index的结点
for (int i = 0; i <= index ; i++) {
cur = cur.next;
}
return cur.val;
}
//头插法添加结点
public void addAtHead(int val) {
ListNode newNode = new ListNode(val, head.next);
head.next = newNode;
length++;
}
//尾插法添加结点
public void addAtTail(int val) {
ListNode newNode = new ListNode(val);
ListNode cur = head;
//循环length次后,cur指向最后一个结点
for (int i = 0; i < length; i++) {
cur = cur.next;
}
cur.next = newNode;
length++;
}
//在index前添加结点val
public void addAtIndex(int index, int val) {
//如果index超出范围则返回
if(index > length) return;
//如果index恰好等于length,则插入末尾并返回
if(index == length){
addAtTail(val);
return;
}
ListNode newNode = new ListNode(val);
//判断index是否符合要求
if(index >= 0 && index <= length - 1){
ListNode cur = head;
//一共循环index次后,cur正好指向索引为index的前一个结点
for (int i = 0; i <= index - 1; i++) {
cur = cur.next;
}
//插入到index的前一个位置
newNode.next = cur.next;
cur.next = newNode;
length++;
}
}
//删除index结点
public void deleteAtIndex(int index) {
//判断index是否符合要求
if(index >= 0 && index <= length - 1){
ListNode cur = head;
//循环index次,cur正好指向索引为index的前一个结点
for (int i = 0; i <= index - 1; i++) {
cur = cur.next;
}
cur.next = cur.next.next;
length--;
}
}
}
206.反转链表
问题简述:得到反转后的链表。
思考:看了视频才知道还有双指针法和递归法,暂时简单看了思路先不写了,等二刷的时候再写。先完成了头插法。
算法思路:将原链表依次头插到新链表中,得到反转链表。
时间复杂度 O(n)
空间复杂度 O(1)
class Solution {
public ListNode reverseList(ListNode head) {
//为新链表创立虚拟头节点
ListNode newhead = new ListNode();
//为给定链表创立虚拟头节点
ListNode cur = new ListNode(0, head);
//当前节点下一个不为空,就把下一个节点头插到新链表
while (cur.next != null){
//记录新链表的头
ListNode p = newhead.next;
//将旧结点插入新链表
newhead.next = cur.next;
//这里我思考过是否会有cur.next.next不存在的情况,因为cur.next != null,即使cur.next.next为空,其仍然存在
cur.next = cur.next.next;
//再次接上新链表的虚拟头节点
newhead.next.next = p;
}
return newhead.next;
}
}
感想
感觉这几道题没用到太复杂的思想,但是稍不留神还是很容易出错,关键在于循环条件。