链表
链表(LinkedList)数据结构:链表存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的.每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成.
链表的好处:相对于传统的数组,链表的一个好处在于,添加或删除元素的时候不需要移动其他元素.在数组中,我们可以直接访问任何位置的任何元素,而想要访问链表中的一个元素则需要从起点(表头)开始迭代链表直到找到所需的元素.
个人理解:链表的数据结构是存的每个节点的数据中有个一字段,这个字段的值是下一个数据节点的地址值,这样就形成了链表结构.其实真正要重点理解的地方是手写实现链表数据结构中自己实现的push、indexOf、removeAt等方法
实现一个链表
class LinkedList{ constructor(){ this.length = 0; this.head = undefined } }
首先创建一个添加节点
class Node { constructor(element){ this.element = element this.next = undefined } }
实现一个push方法:首先判断head是不是undefined是的话push的node节点作为开始节点不是的话current作为临时存储节点变量直到找到最后节点next为undefined然后将node节点赋值给next属性长度+1这样就实现了push方法
LinkedList.prototype.push = function(element){ let node = new Node(element) let current if(this.head == undefined){ this.head = node }else{ current = this.head while(current.next != undefined){ current = current.next } current.next = node } this.length++ }
实现一个根据index下标removeAt的方法:这个方法有意思的地方就在于current最开始代表的是head节点的数据,最后循环层层下移pre与current都一起下移找到了你要remove掉的节点,然后通过next拼接关联起来,然后返回remove掉节点中的element值
LinkedList.prototype.removeAt = function(index){ if(index >= 0 && index < this.length){ let current = this.head if(index === 0){ this.head = current.next }else{ let pre for(let i = 0; i < index; i++){ pre = current; current = current.next } pre.next = current.next//前后拼接 } this.length-- return current.element } return undefined }
实现一个通过下标获取node节点的方法
LinkedList.prototype.getElementAt = function(index){ if(index >= 0 && index < this.length){ let node = this.head for(let i = 0; i < index && node != null;i++){ node = node.next } return node } return undefined }
实现一个任意位置插入的方法:插入就是得到插入位置的pre节点,pre节点的next就是current,让node的next的值为current,再让pre节点的next为node
LinkedList.prototype.insert = function(element,index){
if(index >= 0 &&index <= this.length){
let node = new Node(element)
if(index===0){
let current = this.head
node.next = current
this.head = node
}else{
let pre = this.getElementAt(index-1)
let current = pre.next
node.next = current
pre.next = node
}
this.length++
return true
}
return false
}
实现一个indexOf的方法:这个方法就相对简单了 不解释了
LinkedList.prototype.indexOf = function(element){ let current = this.head for(let i = 0;i < this.length && current != null;i++){ if(element == current.element){ return i } current = current.next } return -1 }
双向链表中实现的方法逻辑都挺难理解的,着重看
双向链表:双向链表和普通链表的区别在于,在链表中,一个节点只有链向下一个节点的链接,而在双向链表中,链表是双向的:一个链向下一个元素,另一个链向前一个元素。
创建一个DoublyNode类
class DoublyNode{ constructor(element,pre){ //在实现的时候 我觉得这个pre直接设为undefined就行了 也没有传参数pre给this.pre赋值 this.pre = pre this.element = element this.next = undefined } }
创建一个双向链表(DoublyLinkedList)类
class DoublyLinkedList{ constructor(){ this.length = 0 this.head = undefined this.tail = undefined } }
实现一个通过下标获取node节点的方法:其实在LinkedList中已经实现过但是由于我不基于LinkedList类实现双向链表所以在DoublyLinkedList类中添加了这个方法
DoublyLinkedList.prototype.getElementAt = function(index){ if(index >= 0 && index < this.length){ let node = this.head for(let i = 0; i < index && node != null;i++){ node = node.next } return node } return undefined }
实现一个在任意位置插入元素的方法
DoublyLinkedList.prototype.insert = function(element,index){ if(index >= 0 && index <= this.length){ let node = new DoublyNode(element) let current = this.head if(index == 0){//从头插 if(this.head == null){ this.head = node this.tail = node }else{ node.next = this.head current.pre = node this.head = node } }else if(index == this.length){//从尾部插 current = this.tail current.next = node node.pre = current this.tail = node }else{//从中间插 let pre = this.getElementAt(index-1) current = pre.next node.next = current pre.next = node current.pre = node node.pre = pre } this.length++ return true } return false }
实现一个任意位置删除元素的方法
DoublyLinkedList.prototype.removeAt = function(index){ if(index >= 0 && index < this.length){ let current = this.head if(index === 0){ this.head = current.next if(this.length === 1){ this.tail = undefined }else{ this.head.pre = undefined } }else if(index === this.length-1){ current = this.tail this.tail = current.pre this.tail.next = undefined }else{ current = this.getElementAt(index) let pre = current.pre pre.next = current.next current.next.pre = pre } return current.element } return undefined }
循环链表:循环链表可以像链表一样只有单向引用也可以向双向链表一样有双向引用。循环链表和链表之间的唯一区别在于,最后一个元素指向下一个元素的指针不是引用undefined,而是指向第一个元素的head
这里只实现单向循环链表中的方法,双向链表循环估计会更不好理解
首先创建一个添加节点
class Node { constructor(element){ this.element = element this.next = undefined } }
实现一个链表
class CircularLinkedList{ constructor(){ this.length = 0; this.head = undefined } }
实现一个通过下标获取node节点的方法:其实在LinkedList中已经实现过但是由于我不基于LinkedList类实现单向循环链表所以在CircularLinkedList类中添加了这个方法
CircularLinkedList.prototype.getElementAt = function(index){ if(index >= 0 && index < this.length){ let node = this.head for(let i = 0; i < index && node != null;i++){ node = node.next } return node } return undefined }
实现一个插入的方法
CircularLinkedList.prototype.insert = function(element,index){ if(index >= 0 && index <=this.length){ let node = new Node(element) let current = this.head if(index === 0){ if(this.head == null){ this.head = node node.next = this.head }else{ node.next = current current = this.getElementAt(this.length-1) this.head = node current.next = this.head } }else{ let pre = this.getElementAt(index-1) node.next = pre.next pre.next = node } this.length++ return true } return false }
实现一个从任意位置删除元素的方法
CircularLinkedList.prototype.removeAt = function(index){ let current = this.head if(index >= 0 && index < this.length){ if(index === 0){ if(this.length == 1){ this.head = undefined }else{ let removed = this.head current = this.getElementAt(this.length-1) this.head = this.head.next current.next = this.head current = removed } }else{ let pre = this.getElementAt(index-1) current = pre.next pre.next = current.next } this.length-- return current.element } return undefined }
有序链表:有序链表是指保持元素有序的链表结构。除了使用排序算法之外,我们还可以将元素插入高正确的位置来保证链表的有序性。
在实现有序链表之前先定义一个对象和实现一个比较函数,在二叉树数据结构中也会用到
let Compare = { LESS_THAN:-1, BIGGER_THAN:1, } function defaultCompare(a,b){ if(a===b){ return 0 }else{ return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN } }
首先创建一个添加节点
class Node { constructor(element){ this.element = element this.next = undefined } }
创建一个有序链表(SortedLinkedList)类
class SortedLinkedList{ constructor(compareFn = defaultCompare){ this.length = 0 this.head = undefined this.compareFn = compareFn } }
SortedLinkedList.prototype.getElementAt = function(index){ if(index >= 0 && index < this.length){ let node = this.head for(let i = 0; i < index && node != null;i++){ node = node.next } return node } return undefined }
实现一个任意位置插入的方法:插入就是得到插入位置的pre节点,pre节点的next就是current,让node的next的值为current,再让pre节点的next为node
SortedLinkedList.prototype.doInsert = function(element,index){ if(index >= 0 &&index <= this.length){ let node = new Node(element) if(index === 0){ let current = this.head node.next = current this.head = node }else{ let pre = this.getElementAt(index-1) let current = pre.next node.next = current pre.next = node } this.length++ return true } return false }
实现一个获取插入位置index的方法
SortedLinkedList.prototype.getIndexNextSortElement = function(element){ let current = this.head let i = 0 // 将变量i在循环体外部声明,即可以循环体内进行return也可以外部return;用var声明也可以 for(;i < this.length && current;i++){ if(this.compareFn(element,current.element) === Compare.LESS_THAN){ // 有序链表的顺序按照从大到小还是从小到大的插入顺序取决于判断比较;Compare.LESS_THAN为从小到大、Compare.BIGGER_THAN为从大到小; return i } current = current.next } return i } SortedLinkedList.prototype.insert = function(element){ if(this.length == 0){ return this.doInsert(element,0) } let position = this.getIndexNextSortElement(element) return this.doInsert(element,position) }
以自己现在的努力程度,还没有资格和别人拼天赋