链表:链表是通过指针串联在一起的线性结构,每个节点有两部分构成(数据域和指针域), 入口节点为 head,最后一个节点指向 null, 有单链表、双链表和循环链表这几种形式。
单链表:指针域只能指向下一个节点;
双链表: 指针一个指向下一个节点,一个指向上一个节点
循环链表: 头尾相连的链表
链表的存储方式: 链表的节点是散乱分布在内存中的某地址上;
链表节点的定义方式:
class ListNode {
val;
next = null;
constructor(value) {
this.val = value;
this.next = null
}
}
203.移除链表元素
我的思路: 需要判断每一个节点的next的值是否等于这个整数,如果等于整数的话,则将这个节点的指针指向next.next;那么如果 head 节点的值 === val, 应该如何识别,这时候需要一个虚拟节点的概念(dummy.next = head)
卡哥实现思路有两种方式:不设虚拟头节点,判断当前节点是否是头节点;设置虚拟头节点,用统一的方式来移除元素;
使用原始方式去移除元素要注意的点:需要判断头节点是否需要删除,
使用虚拟节点的方式来遍历需要注意:1)要建立临时变量来遍历这个链表,不能用头节点进行遍历,如果用头节点进行遍历,头节点指向的值在不断的改变,最后无法返回原先的头节点 2)如果 current.next是要删的元素,那么不需要将 current 指向下一个节点,因为这时还是需要判断 current.next是否=== val
原始的删除操作:
while(head && head.val === val) {
head = head.next
}
if(head === null) return head
let cur = head
while(cur.next) {
if(cur.next.val === val) {
cur.next = cur.next.next
continue
}
cur = cur.next
}
return head
使用虚拟节点的实现代码:
var removeElements = function (head, val) {
let dummy = new ListNode(0, head)
dummy.next = head
let current = dummy
while (current.next) {
if (current.next.val === val) {
current.next = current.next.next
} else {
current = current.next
}
}
return dummy.next
};
707.设计链表
这是一道练习链表操作的题
1. 初始化 linkedList对象
需要定义头节点、尾节点和 size
2, get value of given index from LinkedList
思路:for循环遍历链表,直至找到最后一个节点,即index 所对应的值;这里也用到了虚拟头节点的概念
3. 添加节点在头部
新的头节点 = 定义的val 和指向之前 head 的节点;
注意点: size 要增加;还有链表为空的情况
4. 添加节点在尾部
将当前尾部节点的指针指向需要添加的节点,并将尾部节点定义为添加的节点
5. 添加节点在指定的 index
将 index-1 的节点的 next 指向需要添加的节点,需要添加的节点的 next 指向当前链表 index 对应的节点
6.删除指定 index 的节点
将当前 index-1节点指向 index + 1的节点,注意这里要考虑所有的边界条件,并且 size 要做对应的变化
class ListNode {
constructor(val, next) {
this.val = val;
this.next = next;
}
}
var MyLinkedList = function () {
this._head = null
this._size = 0
this._tail = null
};
MyLinkedList.prototype.getNode = function (index) {
if (index < 0 || index >= this._size) return null
let current = this._head //这个应该返回 head 节点本身
for (let i = 0; i < index; i = i + 1) {
current = current.next
}
return current
}
/**
* @param {number} index
* @return {number}
*/
MyLinkedList.prototype.get = function (index) {
if (index < 0 || index >= this._size) return -1
return this.getNode(index).val
};
/**
* @param {number} val
* @return {void}
*/
MyLinkedList.prototype.addAtHead = function (val) {
let current = new ListNode(val, this._head) // 这里已经定义 next 指向之前的 head 了
this._head = current
if (!this._tail) {
this._tail = current
}
this._size++
};
/**
* @param {number} val
* @return {void}
*/
MyLinkedList.prototype.addAtTail = function (val) {
let current = new ListNode(val, null)
this._size++
if (this._tail) {
this._tail.next = current
this._tail = current
return;
}
this._tail = current
this._head = current
};
/**
* @param {number} index
* @param {number} val
* @return {void}
*/
MyLinkedList.prototype.addAtIndex = function (index, val) {
if (index > this._size) return //如果等于链表 size,应该放在链表末尾
if (index <= 0) {
this.addAtHead(val)
return
}
if (index === this._size) {
this.addAtTail(val)
return
}
const previous = this.getNode(index - 1)
const node = new ListNode(val, previous.next)
previous.next = node
this._size++
};
/**
* @param {number} index
* @return {void}
*/
MyLinkedList.prototype.deleteAtIndex = function (index) {
if (index < 0 || index >= this._size) return
if (index === 0) {
this._head = this._head.next;
if (!this._head) {
this._tail = null
}
this._size--;
return
}
const pre = this.getNode(index - 1)
pre.next = pre.next.next
if (!pre.next) {
this._tail = pre
}
this._size--
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* var obj = new MyLinkedList()
* var param_1 = obj.get(index)
* obj.addAtHead(val)
* obj.addAtTail(val)
* obj.addAtIndex(index,val)
* obj.deleteAtIndex(index)
*/
链表的概念和练习还是不够熟悉,需要反复复习
206.反转链表
思路: 将下一个节点的指针指向当前节点
有几个注意点:
1)改变的是 cur.next, 但是改变下一个节点又要获取未改变的 cur.next所以要暂存一下这个节点, cur到下一个节点 cur=temp
2)头节点要变成最后一个节点,所以要进行暂存,最后返回头节点
3)尾节点指向 null, 不断更新 pre,
var reverseList = function(head) {
let cur = head, temp = null, pre = null;
while(cur) {
temp = cur.next
cur.next = pre //尾节点指向 null, 更新 pre, 以更新当前节点指针指向
pre = cur
cur = temp
}
return pre
};