常见数据结构(栈、队列、链表、树)

栈:
栈是一个线性结构,在计算机中时一个相当常见的数据结构
栈的特点是只能在某一端添加或删除,遵循先进先出的原则
在这里插入图片描述
每种数据结构都可以用很多种方式来实现,其实可以把栈看成是数组的一个子集,所以这里使用数组来实现

class Stack{
	constructor(){
		this.stack=[];	
	}
	push(item){
		this,stack.push(item);_	
	}
	pop(){
		this.stack.pop();	
	}
	peek(){
		return this,stack[this.getCount()-1];
	}
	getCount(){
		return this.stack.length;	
	}
	isEmpty(){
		return this.getCount===0;	
	}
}

应用:
由于只包含字符的字符串’(’,’)’,’{’,’}’,’[‘和’]’,确定输入字符串是有效的。
如果输入字符串有效:
1.必须使用相同类型的括号关闭左括号。
2.必须以正确的顺序关闭打开括号。
请注意,空字符串也被视为有效。
在这里插入图片描述

let isValid=function(s){
	let map={
		'(':-1,
		')':1,
		'[':-2,
		']':2,
		'{':-3,
		'}':3	
	}
	let stack=[];
	for(let i=0;i<s.length;i++){
		if(map[s[i]]<0){
			stack.push(s[i]);
		}else{
			let last=stack.pop();
			if(map[last]+map[s[i]]!=0) return false
		}
	}
	if(stack.length>0) return false;
	return true
};

队列:
队列是一个线性结构,特点是在某一段添加数据,在另一端删除数据,遵循先先进先出的原则。
在这里插入图片描述
实现:
单链队列:

class Queue{
	constructor(){
		this.queue=[];
	}
	enQueue(item){
		this.queue.push(item);
	}
	deQueue(){
		return this.queue.shift();
	}
	getHeader(){
		return this.queue[0];
	}
	getLength(){
		return this.queue.length;	
	}
	isEmpty(){
		return this.getLength()===0	
	}
}

因为单链队列在出栈操作的时候需要O(n)的时间复杂度,所以引入了循环队列。循环队列的出对操作平均是O(1)的时间复杂度。
循环队列:

class SqQueue{
	constructor(length){
		this.queue=new Array(length+1);
		//队头
		this.first=0;
		//队尾
		this.last=0;
		//当前队列的大小
		this.size=0;	
	}
	enQueue(item){
		//判断队尾+1是否为队头
		//如果是就代表需要扩容数据
		//%this.quque.length是为了防止数组越界
		if(this.first===(this.last+1)%this.queue.length){
			this.resize(this.getLength()*2+1);
		}	
		this.queue[this.last]=item;
		this.size++:
		this.last=(this.last+1)%this.queue.length;
	}
	dequeue(){
		if(this.isEmpty()){
			throw Error('Queue is empty');	
		}
		let r=this.queue[this.first];
		this.queue[this.first]=null;
		this.first=(this.first+1)%this.queue.length;
		this.size--;
		//判断当前队列的大小是否过小
		//为了保证不浪费空间,在队列空间等于总长度四分之一时,
		//且不为2时缩小总长度为当前的一半
		if(this.size===this.getLength()/4&&this.getLength()/2!==0){
			this.resize(this.getLength()/2);
		}
		return r;
	}
	getHeader(){
		if(this.isEmpty()){
			throw Error('Queue is empty');	
		}	
		return this.queue[this.first];
	}
	getLength(){
		return this.queue.length-1;
	}
	isEmpty(){
		return this.first===this.last;	
	}
	resize(length){
		let q=new Array(length);
		for(let i=0;i<length;i++){
			q[i]=this.queue[(i+this.first)%this.queue.length]
		}
		this.queue=q;
		this.first=0;
		this.last=this.size;
	}
}

链表:
链表是一个线性结构,同时也是一个天然的递归结构。链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了节点的指针域,空间开销比较大。
在这里插入图片描述
实现:
单向链表:

class Node{
	constructor(v,next){
		this.value=v;
		this.next=next;	
	}
}
class LinkList{
	constructor(){
		//链表长度
		this.size=0;
		this.dummyNode=newNode(null,null);
	}
	find(header,index,currentIndex){
		if(inedx===currentIndex) return header;
		return this.find(header.next,index,currentIndex+1);
	}
	addNode(v,index){
		this.checkIndex(index);
		let prev=this.find(this.dummyNode,index,0);
		//当往链表末尾插入时,prev.next为空
		//其他情况时,因为要插入节点,
		//所以插入的节点的next应该是prev.next
		//然后设置prev.next为插入的节点
		prev.next=next Node(v,prev.next);
		this.size++;
		return prev.next;
	}
	insertNode(v,index){
		return this.addNode(v,index);	
	}
	addToFirst(v){
		return this.addNode(v,0);	
	}
	addToLast(v){
		return this.addNode(v,this.size);	
	}
	removeNode(index,isLast){
		this.checkIndex(index);
		index=isLast?last-1:index;
		let prev=this.find(this.dummyNode,index,0);
		let node=prev.next;
		prev.next=node.next;
		node.next=null;
		this.size--;
		return node;	
	}
	removeFirstNode(){
		return this.removeNode(0);
	}
	removelastNode(){
		return this.removeNode(this.size,0);	
	}
	checkIndex(index){
		if(index<0||index>this.size) throw Error('Index error');
	}
	getNode(index){
		this.checkIndex(index);
		if(this.isEmpty()) return;
		return this.find(this.dummyNode,index,0).index
	}
	isEmpty(){
		return this.size===0;	
	}
	getSize(){
		return this.size;	
	}
}


二叉树:
树拥有很多结构,二叉树是树种最常用的结构,同时也是一个天然的递归结构。
二叉树拥有一个根节点,每个节点至多拥有两个子节点,分别为:左节点和右节点。树的最底部节点称之为叶节点,当一颗树的叶数量为满时,该树可以称为满二叉树。
在这里插入图片描述
二分搜索树:
二分搜索树也是二叉树,拥有二叉树的特征。但是区别在于二叉搜索树每个节点的值都比他的左子树的值大,比他右子树上的值小。
这种存储方式很适合数据搜索。如下图所示,当需要查找6的时候,因为需要查找的值比根节点的值大,所以只需要在根节点的走字数上寻找,大大提高了搜索效率。
在这里插入图片描述
实现:

class Node{
	constructor(value){
		this.value=value;
		this.left=null;
		this.right=null;
	}
}
class BST{
	constructor(){
		this.root=null;
		this.size=0;
	}
	getSize(){
		return this.size;	
	}
	isEmpty(){
		return this.size===0;	
	}
	addNode(v){
		this.root=this._addChild(this.root,v);
	}
	//添加节点时需要比较添加的节点值和当前
	//节点值的大小
	_addChild(node,v){
		if(!node){
			this.size++;
			return new Node(v);	
		}
		if(node.value>v){
			node.left=this._addChild(node.left,v);	
		}else{
			node.right=this._addChild(node.right,v);	
		}
		return node;
	}
}

以上是最基本的二叉搜索树的实现,接下来实现树的遍历
对于树的遍历来说,有三种遍历方式,分别为先序遍历、中序遍历、后序遍历。三种遍历的区别在于何时访问节点。在遍历过程中,每个节点都会遍历三次,分别遍历到自己,遍历左子树和遍历右子树。如果实现先序遍历,那么只需要第一次遍历到节点时进行操作即可。

//先序遍历可用于打印树的结构
//先序遍历先访问根节点,然后访问左节点,最后访问右节点
preTraversal(){
	this._pre(this.root);
}
_pre(node){
	if(node){
		console.log(node.left);
		this._pre(node.left);
		this._pre(node.right);
	}
}
//中序遍历可用于排序
//对于BST来说中序遍历可以实现一次遍历就得到有序的值
//中序遍历表示先访问左节点,然后访问根节点,最后访问右节点
midTraversal(){
	this._mid(this.root);
}
_mid(node){
	if(node){
		this._mid(node.left);
		console.log(value);
		this._mid(node.right);	
	}
}
//后序遍历可用于先操作子节点再操作符节点的场景
//后序遍历表示先访问左节点,然后访问右节点,最后访问根节点。
backTraversal(){
	this._back(this.root);
}
_back(node){
	if(node){
		this._back(node.left);
		this._back(node.right);
		console.log(node.value);
	}
}

以上的这几种遍历都可以称之为深度遍历,对应的还有种遍历叫广度遍历,也就是一层层的遍历。对于广度遍历来说,我们需要利用之前讲过的队列结构来完成。

breadthTraversal(){
	if(!this.root) return null;
	let q=new Queue();
	q.enQueue(this.root);
	while(!q.isEmpty()){
		let n=q.deQueue();
		console.log(n.value);
		if(n.left) q.enQueue(n.left);
		if(n.right) q.enQueue(n.right);
	}
}

接下来先介绍如何在树中寻找最小值或最大值,因为二分搜索树的特性,所以最小值一定在根的最左边,最大值相反

getMin(){
	return this._getMin(this.root).value;
}
_getMin(node){
	if(!node.left) return node;
	return this._getMin(node.left);
}
getMax(){
	return this._getMax(this.root).value;
}
_getMax(node){
	if(!node.right) return node;
	return this._getMax(node.right);
}

转载连接:https://juejin.im/book/5bdc715fe51d454e755f75ef/section/5bdc723a6fb9a049c43d1843

猜你喜欢

转载自blog.csdn.net/qq_41805715/article/details/88124613