数据结构-链表<Typescript>
链表
什么是链表?
链表是一种动态的数据结构,不同于数组的是,链表分配内存空间的灵活性,它不会像数组一样被分配一块连续的内存。当你想在数组的任意位置,插入一个
新值的时候,必须对数组中的各个元素进行相应的位置移动才能达到目标,开销显然是很大的。然而链表的灵活性在于它的每个元素节点分为两部分,一部分
是存储元素本身,另一部分是指向下一个节点元素的引用,也可以称为指针,当你要插入数据时,把上一个节点的向下指针指向新数据节点,新数据节点的向
下指针指向原有数据。但是链表不像数组那样可以直接通过索引立刻定位,只能通过遍历。
定义个node类
/*节点*/ class Node<T>{ public element:T;//节点数据 public next:Node<T> = null;//指向下一个节点 constructor(element:T){ this.element = element; } }
链表的实现
class LinkList<T>{ private head:Node<T> = null;//链表头 private tail:Node<T> = null;//链表尾 private length:number = 0;//链表长度 /* 向链表添加新节点 */ public append(element:T):void{ let node:Node<T> = new Node<T>(element); if(this.length <= 0){//如果当前链表为空,则将head,tail指向node this.head = node; this.tail = node; }else{//否则在链表尾添加新元素 this.tail.next = node; this.tail = node; } this.length++;//链表长度+1 } /* 在链表某位置插入节点 */ public insert(position:number,element:T):boolean{ if(position < 1 || position > this.length + 1){ return false; } let node:Node<T> = new Node<T>(element); if(position == 1){//如果插在第一个位置,将head指向该节点 node.next = this.head; this.head = node; if(this.length == 0){ this.tail = node; } }else if(position == this.length + 1){//如果插在最后,将tail指向该节点 this.tail.next = node; this.tail = node; }else{//剩余是插在链表中间 let prevNode:Node<T> = this.getNodeAt(position - 1); let currentNode:Node<T> = prevNode.next; prevNode.next = node; node.next = currentNode; } this.length++;//链表长度+1 } /* 查找某个元素所在的节点位置 */ public positionOf(element:T):number{ let currentNode:Node<T> = this.head; let currentPosition:number = 1; while(currentNode){ if(currentNode.element == element){ return currentPosition; } currentPosition++; currentNode = currentNode.next; } return -1; } /* 删除某元素节点 */ public remove(element:T):boolean{ let position:number = this.positionOf(element); if(position < 1 || position > this.length){//越界 return false; } let isSuccess:boolean = this.removeAt(position); return isSuccess; } /* 删除某个位置的节点 */ public removeAt(position:number):boolean{ if(position < 1 || position > this.length){//越界 return false; } let currentNode:Node<T> = this.head; if(position == 1){//删除第一个节点 this.head = currentNode.next; if(this.length == 1){ this.tail = null; } }else{ let prevNode:Node<T> = this.getNodeAt(position - 1); prevNode.next = prevNode.next.next; if(position == this.length){ this.tail = prevNode; } } this.length--;//链表长度减一 return true; } /* 返回某位置的节点*/ public getNodeAt(position:number):Node<T>{ if(position < 1 || position > this.length){//越界 return null; } let currentNode:Node<T> = this.head; let currentPositon:number = 1; while(currentPositon < position){//遍历链表,找到节点 currentNode = currentNode.next; currentPositon++; } return currentNode; } /* 链表是否为空 */ public isEmpty():boolean{ return this.length == 0; } /* 返回链表长度 */ public size():number{ return this.length; } /* 返回链表头 */ public getHead():Node<T>{ return this.head; } /* 返回链表尾 */ public getTail():Node<T>{ return this.tail; } /* 清空链表 */ public clear():void{ this.head = null; this.tail = null; this.length = 0; } /* 打印链表 */ public print():string{ let currentNode:Node<T> = this.head; let str:string = ""; let currentPostion:number = 1; while(currentNode){ if(currentPostion < this.length){ str += `element:${currentNode.element} ---- `; }else{ str += `element:${currentNode.element}`; } currentNode = currentNode.next; currentPostion++; } return str; } }
测试
let list:LinkList<string> = new LinkList<string>(); list.append("电视剧"); list.append("天地男儿"); list.append("刑事侦缉档案"); list.append("西游记"); list.print(); list.remove("电视剧"); list.print(); list.insert(1,"TVB"); list.print(); list.removeAt(2); list.print();
双向链表
上面链表称为单链表,每一个元素有一个next指针,指向下一个节点,我们只能从链表的头部开始遍历整个链表,任何一个节点只能找到它的下一个节点,
而不能找到它的上一个节点。双向链表中的每一个元素拥有两个指针,一个用来指向下一个节点,一个用来指向上一个节点。在双向链表中,除了可以像
单向链表一样从头部开始遍历之外,还可以从尾部进行遍历。
//节点
/* 节点 */ class Node<T>{ public element:T; public next:Node<T>; public prev:Node<T>; constructor(element:T){ this.element = element; } }
//实现
/* 双向链表 */ class DoubleList<T>{ private length:number = 0; private head:Node<T> = null; private tail:Node<T> = null; /* 向链表添加元素为element的节点 */ public append(element:T):void{ let node:Node<T> = new Node<T>(element); if(this.length <= 0){ this.head = node; this.tail = node; }else{ this.tail.next = node; node.prev = this.tail; this.tail = node; } this.length++;//链表长度+1 } /* 在链表某位置插入节点 */ public insert(position:number,element:T):boolean{ if(position < 1 || position > this.length + 1){ return false; } let node:Node<T> = new Node<T>(element); if(position == 1){//如果插在第一个位置,将head指向该节点 node.next = this.head; this.head.prev = node; this.head = node; if(this.length == 0){ this.tail = node; } }else if(position == this.length + 1){//如果插在最后,将tail指向该节点 this.tail.next = node; node.prev = this.tail; this.tail = node; }else{//剩余是插在链表中间 let prevNode:Node<T> = this.getNodeAt(position - 1); let currentNode:Node<T> = prevNode.next; prevNode.next = node; node.prev = prevNode; node.next = currentNode; currentNode.prev = node; } this.length++;//链表长度+1 } /* 查找某个元素所在的节点位置 */ public positionOf(element:T):number{ let currentNode:Node<T> = this.head; let currentPosition:number = 1; while(currentNode){ if(currentNode.element == element){ return currentPosition; } currentPosition++; currentNode = currentNode.next; } return -1; } /* 删除某元素节点 */ public remove(element:T):boolean{ let position:number = this.positionOf(element); if(position < 1 || position > this.length){//越界 return false; } let isSuccess:boolean = this.removeAt(position); return isSuccess; } /* 删除某个位置的节点 */ public removeAt(position:number):boolean{ if(position < 1 || position > this.length){//越界 return false; } let currentNode:Node<T> = this.head; let prevNode:Node<T>; if(position == 1){//删除第一个节点 this.head = currentNode.next; if(this.head){ this.head.prev = null; } if(this.length == 1){ this.tail = null; } }else if(position == this.length){//删除最后一个节点 prevNode = this.tail; prevNode.next = null; this.tail = prevNode; }else{ prevNode = this.getNodeAt(position - 1); prevNode.next = prevNode.next.next; prevNode.next.prev = prevNode; } this.length--;//链表长度减一 return true; } /* 返回某个位置的节点 */ public getNodeAt(position:number):Node<T>{ if(position < 1 || position > this.length){ return null; } let currentNode:Node<T>; let currentPosition:number = 1; //从后往前遍历 if(position > Math.floor(this.length / 2)){ currentNode = this.tail; while(currentPosition < this.length - position + 1){ currentNode = currentNode.prev; currentPosition++; } return currentNode; }else{//从前往后 currentNode = this.head; while(currentPosition >= this.length){ currentNode = currentNode.prev; currentPosition++; } return currentNode; } } /* 链表是否为空 */ public isEmpty():boolean{ return this.length == 0; } /* 返回链表长度 */ public size():number{ return this.length; } /* 返回链表头 */ public getHead():Node<T>{ return this.head; } /* 返回链表尾 */ public getTail():Node<T>{ return this.tail; } /* 清空链表 */ public clear():void{ this.head = null; this.tail = null; this.length = 0; } /* 打印链表 */ public print():string{ let currentNode:Node<T> = this.head; let str:string = ""; let currentPostion:number = 1; let prevElement:T; let nextElement:T; while(currentNode){ prevElement = currentNode.prev ? currentNode.prev.element : null; nextElement = currentNode.next ? currentNode.next.element : null; str += `[prev:${prevElement},element:${currentNode.element},next:${nextElement}] `; currentNode = currentNode.next; currentPostion++; } console.log(str + "\n"); return str; } }
//测试
let list:DoubleList<string> = new DoubleList<string>(); list.append("寻秦记"); list.append("大唐双龙传"); list.append("四大名捕"); list.append("笑傲江湖"); list.append("圆月弯刀"); list.append("布衣神相"); list.print(); list.removeAt(1); list.print(); list.removeAt(5); list.print();