链表
链表:
链表实际上是线性表的链式存储结构,与数组不同的是,它是用一组任意的存储单元来存储线性表中的数据,存储单元不一定是连续的,
且链表的长度不是固定的,链表数据的这一特点使其可以非常的方便地实现节点的插入和删除操作
链表的每个元素称为一个节点,每个节点都可以存储在内存中的不同的位置,为了表示每个元素与后继元素的逻辑关系,以便构成“一个节点链着一个节点”的链式存储结构。
let LinkNode=function(val){
this.val=val;
this.next=null;
}
//链表
function LinkList(){
this.length=0;
this.head=null;
}
//增加节点
LinkList.prototype.append=function(val){
let node=new LinkNode(val)
if(this.head==null) {
this.head=node
}
else{
let current=this.head;
//找到最后一个节点
while(current.next!=null){
current=current.next;
}
current.next=node;
}
this.length++;
}
//查找到指定位置节点
LinkList.prototype.getNodeAt=function(index){
if(index<0||index>this.length-1) return null;
let current=this.head;
while(index>0){
current=current.next;
index--;
}
return current;
}
//查找指定元素所在节点索引
LinkList.prototype.IndexOf=function(val){
let count=0
let current=this.head;
while(count<this.length){
if(current.val==val) return count;
current=current.next;
count++;
}
return -1;
}
//在指定位置插入节点
LinkList.prototype.insert=function(position,val){
if(position<0||position>this.length-1) return false;
let node=new LinkNode(val);
if(position==0){
node.next=this.head;
this.head=node;
}else{
let pre=this.getNodeAt(position-1);
node.next=pre.next;
pre.next=node;
}
this.length++;
return true;
}
//删除指定位置节点
LinkList.prototype.removeAt=function(position){
if(position<0||position>this.length-1) return false;
if(position==0){
this.head=this.head.next;
}else{
let pre=this.getNodeAt(position-1);
pre.next=pre.next.next
}
this.length--;
return true;
}
//打印
LinkList.prototype.toString=function(){
let res=''
let current=this.head
let size=this.length;
while(size>0){
let nextValue=current.next?current.next.val:null
res+=`{Nodevalue:${current.val} next:${nextValue } }`
size--;
current=current.next;
}
return res
}
//翻转链表
LinkList.prototype.reverse=function(){
if(this.head==null) return null;
let p=this.head;
let q=null;
while(p.next!==null){
q=p.next;
p.next=q.next;
q.next=this.head
this.head=q
}
return this.head
}
/*判断有没有环,环的入口点,环的长度
1、设置快慢指针,当快指针第一次和慢指针相遇时,可以判断有环
2、为了求入口点,可以在相遇时再设置一个指向头结点的指针,让
慢指针和新的指针一起走,相遇点就是环入口点
3、环长度可以让快慢指针相遇后,再次出发,第二次相遇时慢指针
走过的步数就是环长度
*/
//判断环
LinkList.prototype.hasCycle=function(){
let slow=this.head;
let fast=this.head;
while(fast.next!=null&&fast!=null){
slow=slow.next;
fast=fast.next.next;
if(fast==slow) return true;
}
return false
}
//判断入口点
LinkList.prototype.cycleEnter=function(){
let slow=this.head;
let fast=this.head;
while(fast.next!=null&&fast!=null){
slow=slow.next;
fast=fast.next.next;
if(fast==slow) break;
}
if(fast.next==null||fast==null) return null;
fast=this.head;
while(fast!=slow){
fast=fast.next;
slow=slow.next;
}
return fast;
}
//判断环长度
LinkList.prototype.cycleLength=function(){
let slow=this.head;
let fast=this.head;
let size=1;
while(fast.next!=null&&fast!=null){
slow=slow.next;
fast=fast.next.next;
if(fast==slow) break;
}
if(fast.next==null||fast==null) return 0;
fast=fast.next.next;
slow=slow.next;
while(slow!=fast){
size++;
fast=fast.next.next;
slow=slow.next;
}
return size;
}
var list=new LinkList()
list.append('1')
list.append('2')
list.append('3')
list.append('4')
console.log(list)
console.log(list.getNodeAt(2))
console.log(list.IndexOf('4'))
list.insert(0,'0')
console.log(list.toString())
list.reverse();
console.log(list.toString())
//构建有环链表
var list2=new LinkList()
list2.append('1')
list2.append('2')
list2.append('3')
list2.append('4')
list2.getNodeAt(list2.length-1).next=list2.getNodeAt(1)
console.log(list2.toString())
console.log(list2.hasCycle())
console.log(list2.cycleEnter())
console.log(list2.cycleLength())
结果: