数据结构和算法(js版)
1.1.什么是数据结构?
数据结构就是在计算机中,存储和组织数据的方式。
例如:图书管理,怎样摆放图书才能既能放很多书,也方便取?
主要需要考虑两个问题:
操作一:新书怎么插入?
操作二:怎么找到某本指定的书?
常见的数据结构
数组(Aarray)
栈(Stack)
链表(Linked List)
图(Graph)
散列表(Hash)
队列(Queue)
树(Tree)
堆(Heap)
注意:数据结构与算法与语言无关,常见的编程语言都有直接或间接的使用上述常见的数据结构。
1.2.什么是算法?
算法(Algorithm)的定义
一个有限指令集,每条指令的描述不依赖于语言; 接收一些输入(有些情况下不需要输入); 产生输入; 一定在有限步骤之后终止; 算法(Algorithm)通俗理解:解决问题的办法/步骤逻辑。数据结构的实现,离不开算法
1.js数组可以存放不同的数据类型,常见语言的数组不能存放不同的数据类型,因此所有再封装时通常存放再数组中的是Object类型
2.常见语言的数组容量不会自动改变(需要进行扩容操作),js底层可以自动扩容
3.常见语言的数组进行中间插入和删除操作性能比较低,修改和查找下标某个元素效率比较高
2.栈结构
数组是一个线性结构,并且可以再数组的任意位置插入和删除元素,而栈和队列就是比较常见的受限的线性结构
栈是一种受限的线性表,后进先出(LIFO)
其限制是仅允许在表的一端进行插入和删除运算,这一端被称为栈顶,相对的另一端被称为栈底。
LIFO(last in first out)表示就是后进入的元素,第一个弹出栈空间,类似于自动餐托盘,最后放上去的,往往先拿出去使用
向一个栈中插入新元素又称作进栈,入栈或压栈 ,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;
从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素,成为新的栈顶元素
image-20200226131817102
栈的特点是:先进后出,后进先出
程序中的栈结构:
函数调用栈:A(B(C(D()))):即A函数中调用B,B调用C,C调用D;在A执行的过程中会将A压入栈,随后B执行时B也被压入栈,函数C和D执行时也会被压入栈。所以当前栈的顺序为:A->B->C->D(栈顶);函数D执行完之后,会弹出栈被释放,弹出栈的顺序为D->C->B->A;
递归:为什么没有停止条件的递归会造成栈溢出?比如函数A为递归函数,不断地调用自己(因为函数还没有执行完,不会把函数弹出栈),不停地把相同的函数A压入栈,最后造成栈溢出(Stack Overfloat)
3.练习:题目:有6个元素6,5,4,3,2,1按顺序进栈,问下列哪一个不是合法的出栈顺序?
A:5 4 3 6 1 2 (√) B:4 5 3 2 1 6 (√) C:3 4 6 5 2 1 (×) D:2 3 4 1 5 6 (√) 题目所说的按顺序进栈指的不是一次性全部进栈,而是有进有出,进栈顺序为6 -> 5 -> 4 -> 3 -> 2 -> 1。
解析:
A答案:65进栈,5出栈,4进栈出栈,3进栈出栈,6出栈,21进栈,1出栈,2出栈(整体入栈顺序符合654321); B答案:654进栈,4出栈,5出栈,3进栈出栈,2进栈出栈,1进栈出栈,6出栈(整体的入栈顺序符合654321); C答案:6543进栈,3出栈,4出栈,之后应该5出栈而不是6,所以错误; D答案:65432进栈,2出栈,3出栈,4出栈,1进栈出栈,5出栈,6出栈。符合入栈顺序;
栈的常见操作:
push():添加一个新元素到栈顶位置;
pop(): 移除栈顶的元素,同时返回被移除的元素
peek():返回栈顶的元素,不对栈做任何修改(该方法,不会移除栈顶元素,仅仅返回它)
isEmpty() : 如果栈里面没有任何元素就返回true,否则就返回false
size() : 返回栈里的元素个数,这个方法和数组的length属性类似
toString() : 将栈结构的内容以字符串形式返回
实现栈结构有两种比较常见的方式:
基于数组实现
基于链表实现
封装栈(基于数组实现):
// Method:和某个对象实例有联系的,称为方法
// function:
// 栈封装类
function Stack(){
// 栈中的属性
this.items = [];
// 栈的相关操作
// 1.将元素压入栈
// this.push = function (){
// } //使每个实例都有一个方法,不推荐,占用内存
Stack.prototype.push = function (element){
this.items.push(element);
} //放在原型上共享,推荐使用
// 2.从栈中取出元素
Stack.prototype.pop = function(){
return this.items.pop()
}
// 3.查看一下栈顶元素
Stack.prototype.peek = function(){
return this.items[this.items.length -1]
}
// 4.判断栈是否为空
Stack.prototype.isEmpty = function(){
return this.items.length == 0
}
// 5.获取栈中元素的个数
Stack.prototype.size = function(){
return this.items.length
}
// 6.toString方法
Stack.prototype.toString = function(){
var resultString = ' ';
for (var i=0;i<this.items.length;i++){
resultString += this.items[i] + ' ';
}
return resultString
}
}
// 栈的使用
var s = new Stack();
s.push(20);
s.push(100);
s.push(1);
console.log(s);
console.log(s.peek());
s.pop();
console.log(s);
console.log(s.isEmpty());
console.log(s.size());
使用ES6的方式封装
class Stack{
constructor(){
// 属性
this.count = 0; //使用count 属性来帮助我们记录栈的大小(也帮助我们从数据结构中添加和删除元素)
this.items = {};
}
// 方法
// 往栈中添加元素
push(element){
this.items[this.count] = element;
this.count++;
};
// 验证一个栈的大小
size(){
return this.count;
};
// 要验证栈是否为空
isEmpty(){
return this.count === 0;
};
// 弹出栈顶元素
pop(){
if(this.isEmpty()){
return undefined;
}
this.count--;
const result = this.items[this.count];
delete this.items[this.count];
return result;
};
// 查看栈顶的值,并将栈清空
peek(){
if(this.isEmpty()){
return undefined;
}
return this.items[this.count -1];
};
// 清空栈
clear(){
// this.items = {};
// this.count = 0;
while (!this.isEmpty()){
this.pop();
}
};
// toString()方法
toString(){
if(this.isEmpty()){
return '';
}
let objString = `${this.items[0]}`;
for(let i=1;i<this.count;i++){
objString = `${objString},${this.items[i]}`;
}
return objString;
}
}
将十进制转为二进制:
// 将十进制转为二进制
function decTobin(decNumber){
// 1.定义一个栈对象
var s = new Stack();
// 2.循环数组大于0时
while(decNumber / 2 >0){
// 2.1获取余数,并且放入到栈中
s.push(decNumber % 2);
// 2.2获取整除后的结果,作为下一次运行的数字
decNumber = Math.floor(decNumber / 2)
}
// 3.从栈中取出0和1,从上到下取
var newString = '';
while(!s.isEmpty()){
newString +=s.pop();
}
return newString;
}
console.log(decTobin(10));
console.log(decTobin(100));
console.log(decTobin(1000));