学习内容
链表的定义和类型
链表是通过指针串联在一起的线性结构,链表元素的存储是不连续的。链表的入口节点称为链表的头结点也就是head。
常见链表的类型:
- 单链表
{val, next} - 双链表
{prev, val, next} - 循环链表 ,链表收尾相连
tail.next = head
习题总结
203.移除链表元素
虚拟头结点的问题
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} val
* @return {ListNode}
*/
var removeElements = function (head, val) {
// 这种情况当head.val === val时,没删除head
// let node = head
// 所以要假立一个新的空head
const listHead = new ListNode(0)
listHead.next = head
let node = listHead
while (node&&node.next) {
if (node.next.val === val) {
node.next = node.next.next
} else {
node = node.next
}
}
return listHead.next
}
707.设计链表
js中不存在链表
可以通过数组或者对象模拟链表
数组有点偷懒
- 使用对象模拟链表,设置head和tail指针,size属性
- 时刻要考虑边界问题!!!注意更改指针指向
class LinkNode {
constructor(val, next) {
this.val = val === undefined ? '' : val
this.next = next === undefined ? '' : next
}
}
function MyLinkedList() {
this._size = 0
this._head = null
this._tail = null
}
/**
* @param {number} index
* @return {number}
*/
MyLinkedList.prototype.get = function (index) {
if (index >= this._size || index < 0) return -1
let node = this._head
while (index > 0) {
node = node.next
index--
}
return node ? node.val : null
};
/**
* @param {number} val
* @return {void}
*/
MyLinkedList.prototype.addAtHead = function (val) {
const node = new LinkNode(val, this._head)
this._size++
this._head = node
if (this._size === 1) {
this._tail = node
}
return node
};
/**
* @param {number} val
* @return {void}
*/
MyLinkedList.prototype.addAtTail = function (val) {
const node = new LinkNode(val, null)
if (this._tail) {
this._tail.next = node
} else {
this._head = node
}
this._tail = node
this._size++
return this._head
};
/**
* @param {number} index
* @param {number} val
* @return {void}
*/
MyLinkedList.prototype.addAtIndex = function (index, val) {
if (index > this._size || index < 0) return -1
if (index === this._size) {
this.addAtTail(val)
} else if (index === 0) {
this.addAtHead(val)
} else {
let prevNode = this._head
while (index > 1) {
prevNode = prevNode.next
index--
}
const node = new LinkNode(val, prevNode.next)
prevNode.next = node
this._size++
}
return this._head
};
/**
* @param {number} index
* @return {void}
*/
MyLinkedList.prototype.deleteAtIndex = function (index) {
if (index >= this._size || index < 0) return null
this._size--
if (this._size === 0) {
this._head = null
this._tail = null
} else if(index === 0){
this._head = this._head.next
} else{
let preNode = this._head
while (index > 1) {
preNode = preNode.next
index--
}
if (preNode.next.next) {
preNode.next = preNode.next.next
} else {
preNode.next = null
this._tail = preNode
}
}
return this._head
};
/**
* 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.反转链表
方法一:双指针法 prev 和 cur 一前一后
注意:最后返回的是pre,迭代到最后,cur是空
- 把next节点存起来temp
- 把当前节点的next指向prev
- 把prev往前走 = cur
- 把cur往前走 = temp
/**
* @param {ListNode} head
* @return {ListNode}
*/
var reverseList = function(head) {
let cur = head
let prev = null
while(cur){
const temp = cur.next
cur.next = prev
prev = cur
cur = temp
}
return prev
};
方法二:递归 思路:递归几乎和双指针一样,无非就是把while循环变成了递归调用,注意:递归调用记得返回
/**
* @param {ListNode} head
* @return {ListNode}
*/
var reverseList = function(head) {
const reverse = (cur, prev)=>{
if(!cur) return prev
const temp = cur.next
cur.next = prev
return reverse(temp, cur)
}
return reverse(head,null)
};
链表初步总结:
- 循环使用while方便
- 虚拟头结点的使用
- 双指针翻转链表
- 尤其要注意边界问题
- 要总结class和function的区别