JS中的设计模式与数据结构之单链表

链表

Node类

function Node(x){
	//节点的值
	this.value = x;
	//节点的下一个指向
	this.next = null;
}

NodeList类

function NodeList(){
	//初始化定义首节点
	this.head = new Node('head');
	//定义find方法,返回查找的节点
	this.find = find;
	//定义insert方法,在某个节点后插入一个节点
	this.insert = insert;
	//定义display方法,返回一个Map展示当前链表
	this.display = display;
	//定义remove方法,根据value值移除一个指定节点
	this.remove = remove;
	//定义removeByNO方法,根据序号移除一个指定节点
	this.removeByNO = removeByNO;
	//定义reverse方法,翻转链表
	this.reverse = reverse;
}

find方法

function find(target){
	//将该链表的首节点赋值给创建的新节点
	let currentNode = this.head;
	//判断节点的值是不是我们要的
	while(currentNode.value != target){
		//如果不是,先考虑这是不是最后一个节点
		if(currentNode.next == null){
			//若为最后一个节点,则查找失败
			return 'not find'
		}else{
			//若不是,继续查找下一个节点
			currentNode = currentNode.next;
		}
	}
	//如果查找成功,返回该节点
	return currentNode;
}

//创建一个名为nodeList的链表
var nodeList = new NodeList();
nodeList.find('he52ad');//not find
nodeList.find('head');//Node {value: "head", next: null}

insert方法

向链表中插入一个节点的效率很高,需要修改它前面的节点(前驱),使其指向新加入的节点,而将新节点指向原来前驱节点指向的节点即可。下面演示如何在 data2 节点后面插入 data4 节点。
在这里插入图片描述

function insert(value,target){
	//创建一个新的节点
	let newNode = new Node(value);
	//利用我们写的find方法,先判断一下指定节点是否存在
	if(this.find(target) == 'not find'){
		//不存在则直接返回
		return 'not find '+target;
	}
	//若存在,则通过find方法拿到指定的某个节点
	let lastNode = this.find(target);
	//让新节点指向指定节点的下一个节点
	newNode.next = lastNode.next;
	//指定节点指向新节点
	lastNode.next = newNode;
}

display方法

我喜欢将链表的遍历结果存入一个Map结构中,一个显著好处是对该结果的操作将更为便利和直观,如果你不喜欢这样,你可以自己手写display方法将其存入数组。

function display(){
	//新建一个名为nodeListDisplay 的Map结构
	let nodeListDisplay = new Map();
	//新建一个当前结点,并将该链表的头节点赋值过来
	let currentNode = this.head;
	//先塞入头节点至Map中
	nodeListDisplay.set(0,currentNode.value);
	//设定一个变量i,作为序号,因为头节点是0了,所以从1开始递增
	let i = 1;
	//当下个节点不为空时执行,若为空停止
	while(currentNode.next != null){
		//拿到下一个节点
		currentNode = currentNode.next;
		//下一个节点写入Map结构
		nodeListDisplay.set(i,currentNode.value);
		//序号递增
		i++;
	};
	return nodeListDisplay;
}

var nodeList = new NodeList();
nodeList.insert('first','head');
nodeList.insert('second','first');
nodeList.insert('third','second');
//将返回的map结果存入dis变量
var dis = nodeList.display();
//现在你可以直接遍历了
for (let value of dis.values()) {
  console.log(value);
}					//head
					//fisrt
					//second
					//third
//或者,利用扩展运算符(...),快速将map结构转为数组
console.log([...dis.keys()]);//[0, 1, 2, 3]
console.log([...dis.values()]);//["head", "first", "second", "third"]

remove方法

从链表中删除一个节点,也很简单。只需将待删节点的前驱节点指向待删节点的,同时将待删节点指向null,那么节点就删除成功了。下图片演示如何从链表中删除 data4 节点。
在这里插入图片描述

function remove(target){
	//利用我们写的find方法,先判断一下指定节点是否存在
	if(this.find(target) == 'not find'){
		//不存在则直接返回
		return 'not find '+target;
	}
	//若存在,则创建一个名为deleteNode的变量保存这个要删除的节点
	let deleteNode = this.find(target);
	//下一步是寻找这个要删除节点的前驱节点,首先从头节点开始找
	let previousNode = this.head;
	//如果首节点的值等于target
	if( previousNode.value === target ){
		//即删除首节点,将首节点的下一个值当作新的首节点
		this.head = this.head.next;
		return;
	}
	//如果不是删除首节点,则循环判断previousNode节点的下一个节点值是不是等于target
	while(previousNode.next.value !== target){
		//如果不等,说明previousNode不是target的前驱节点
		previousNode = previousNode.next;
	}
	//当找到时,将要删除节点的下一节点赋值给前驱节点的next,完成删除
	previousNode.next = deleteNode.next;
}

removeByNO方法

或者我们更进一步,写一写通过序号删除节点的方法

function removeByNO(number){
	let size = this.display().size;
	let deleteNode = this.head.next;
	let previousNode = this.head;
	if( number>size || number<0 ){
		return 'out of index'
	}
	if( number==0 ){
		this.head = this.head.next;
		return;
	}else{
		for( let i=1;i<=size;i++ ){
			if( i==number ){
				previousNode.next = deleteNode.next;
			}else{
				previousNode = previousNode.next;
				deleteNode = deleteNode.next;
			}
		}
	}
}

//测试一下
var nodeList = new NodeList();
nodeList.insert('first','head');
nodeList.insert('second','first');
nodeList.insert('third','second');
console.log(nodeList.display());//Map(4) {0 => "head", 1 => "first", 2 => "second", 3 => "third"}
nodeList.removeByNO(0);
console.log(nodeList.display());//Map(3) {0 => "head", 1 => "second", 2 => "third"}

reverseByValue方法

这一种反转方式实际上只改变了每个节点的值,并没有对节点做移动

function reverse()
{
    let currentNode=this.head, arr=[];
    while(currentNode!=null){
        arr.push(currentNode.value);
        currentNode=currentNode.next;
    }
    currentNode= this.head;
    while(currentNode!=null){
        currentNode.value = arr.pop();
        currentNode= currentNode.next;
    }
    return this.display();
}

reverse方法

这种反转方式移动了节点

function reverse()
{
    let p = this.head;
    let q = this.head.next;
    this.head.next = null;
    while( q!= null){
        let r = q.next;
        q.next = p;
        p = q;
        q = r;
    }
    this.head = p;
    return this.display();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值