1.栈的概念
自己的理解是:栈的结构是一种后进先出的结构(遵循 LIFO 原则),栈的操作有入栈出栈,栈顶,栈底,检查栈顶,清空检查个数这种操作。栈实现了一个进制装换算法:余数法。
栈的作用:在编程语言的编译器和内存中保存变量,方法和调用。栈和函数(函数调用时入栈跟出栈)。栈是一个基本的计算机数据结构是高级编程语言的实现基础。
递归:如果是不停的递归而不出栈,会导致栈溢出,后面死机了。递归的意思就是函数调用自己本身。
问题:1.栈和队列的区别:
2.tcp/ip协议和http协议是啥
3冒泡排序和归并排序的区别
var stack = function(){ var items = [] this.push = function(element){ items.push(element) } } // pop this .pop = function(){ return items.pop() } //peek 检查栈顶 this.peek = function(){ return items[item.length-1] } //检查栈 是否为空 this.isEmpty = function(){ return items.length == 0 } //清除线 this.clear = function(){ items = [] } //获取栈的大小 this.size = function(){ return items.length } //检查items this.getItems = function(){ return items } //10转2 var divBy2 = function(number){ var stack = new Stack() var yushu var string2="" while(number > 0){ yushu = number % 2 stack.push(yushu) number = Math.floor(number / 2) } while(!stack.isEmpty()){ string2 += stack.pop() } return string2 }
2.队列的概念
队列是使用类来封装队列 操作
添加在最后一项push,删除下标索引号第0个用shift(头部),删除下标索引号最后一个用pop(尾巴)
先进先出
优先级 priorityQueue
splice 切割
队列 =》 优先队列 var PriorityQueue = function(){ var items = [] } //辅助类 var QueueItem = function(element,priority ){ this.element = element this.priority = priority } this.enqueue = function (element,priority) { var QueueItem = new QueueItem(element,priority) var added = false for(var i = 0; i < items.length ; i++){ if(QueueItem.priority > items[i].priority){ items.splice(i, 0, QueueItem) added = true break } } if (!added){ items.push(QueueItem) } this.getItems = function () { return items } } var pq = new PriorityQueue() pq.enqueue("小黑",10) pq.enqueue("小明",12)
var Queue = function(){ var item =[] //入队 this.enqueue = function(element){ item.push(element) } //出队 this.dequeue = function(){ return item.shift(element) } //查看队列头 this.front = function(){ return item(0) } //检查队列是否为空 this.isEmpty = function(){ return items.length === 0 } //检查队列的大小 this.size = function(){ return items.length() } }
3.链表
什么是链表:可以想成火车,火车头连接到连接到火车尾巴。一环扣一环。链表是没有下标的。
链表头插入元素:insert(position,element)
链表尾获取元素索引:indexsof(element)
尾部添加元素:append(element)
复用代码:remove()链表中移除一项;indexof()获取元素索引;removeAt()链表中特定位置移除
var LikedList = function(){
//链表头
var head = null //object nulll
//链表长度
var length = 0
//辅助类节点
var Node = function(element){
this.element = element
this.next = null
}
//链表尾部添加元素
this.apped = function(element){
var node = new Node(element)
//nade ={element:element next :null}
}
if (head == null){
head = node
} else {
var current = head
while(current.next){
current = current.next
}
//while 循环执行之后,current已经是链表最后一项
current.next = node
}
length++
}
this.getHead = function(){
return head
}
current=head 意思是当前
//链表的某个位置添加元素 this.insert = function(position,element){ //越界问题 if(position > -1 && position < length){ var node = new Node(element) if(position == 0){ var current = head; head = node head.next = current } else { var index = 0 var current = head var previous = null while(index < position){ previos = current current = current.next index++ } previous.next = node node.nexs = current } length++ } } this.getHead = function(){ return head } var l = new LikedList l.apped(1) l.apped(2) l.apped(3) l.insert (1,10) //1 10 2 3
作业练习代码:
function Node(name, next) {
this.name = name;
this.next = next;
}
// 制造数据
var C = new Node("C",null);
var B = new Node("B",C);
var A = new Node("A",B);
// 创建链表
A
// 在A后插入D
var D = new Node("D",null);
console.log(A.next.name);//B
var tmp = A.next;
A.next = D;
D.next = tmp;
// 作业:改为只有A(表头)
A.next.name=A.next.next.name
A.name = A.next.name
// 删除B
D.next = C;
// 作业:改为只有A(表头)
// 输出所有的结点的name
console.log(A.name);
console.log(A.next.name);
console.log(A.next.next.name);
var tmp = A;
while(tmp!=null) {
console.log(tmp.name);
tmp = tmp.next;
}
常见的报错。另外学习DMA中断 操作系统,资源竞争,科学家就餐,生产者消费者,银行排队,分时时间片等的算法。
4,集合
- 集合概念:集合是一种数学中的概念。特性:(1)无重复性。(2)空集。(3)子集。
- 我们用来构建集合框架的东西就是对象:var set = function(){var item = {};}
var Set2 = function(){ var item ={} //has 检查元素是否存在 return boolean this.has = function(){ return items.hasOwnProperty(value) } //add添加元素,集合不重复 this.add = function(value){ //value = 3 // if (!this.has) if (!this.has(value)){ //true 存在 return false } else{ //false 不存在 item[value] = value //items[3]=3 //方括号语法,点语法 return value } } //移除元素 this.remove = function(value) { if(this.has(value)){ //拿出来用 delete items[value] return true } else{ //不管用 return false } } //清空集合 this.clear = function(){ items={} } //size集合长度大小 this.size = function () { //遍历集合 var count = 0 for(var i in item){ count++; console.log(i); } return count } //检查程序 this.getItems = function(){ return items } }
5,字典散列表
- 字典的学习是一种类似集合的数据结构
- 键值对
- 字典的主要操作:添加键值对:set(key,value)通过键值对移除元素 delete(key) 检查健has(key) 由健获取值get(key) 。用 JavaScript 的 in 运算符来验证给定元素是否是 items 对象的属性如“name”in book 或‘price’ in book
- 散列表操作方法:添加元素put(key,value),移除值remove(key),检索值get(key)
- export default class Dictionary 导出默认类字典
- 哈希表也叫散列表,有分离链接法,和线性探性查法
- loseloseHashCode是将一个key散列值出一个算法,存在缺陷,重复率太高,所以我们才要用分离链接法和线性探查法来避免这种重复。
- 分离链接法是结合了链表
- 线性探查
- 栈跟队列是通过数组实现的,链表是通过字面量实现的,集合跟字典是通过对象实现的
6,树
- 树结构的核心就是一个个节点辅助类
- 我,们遍历树的话必须有个递归,所以我们要有个辅助函数,对外部不可暴露复杂的方法,所以只能用简单的方法操作他,内部相对复杂一点。(1)添加节点。(2)查找节点。(3)遍历节点。(4)删除节点
- 二叉树定律
- 插入数据的时候要判断树是不是空的,树是空就设置为根节点,树不是空的,对比值。Root是null也就说明树是空的。如果不是空就对比值大小在对应的位置
- 什么叫递归呢:var fn=function(){fn()}fn() 调用自己本身 1,递归的出口在哪里
插入节点: var root = null; //插入节点 var insertNode = function (node, newNode) { //首先要实现个方法,它接受两个参数一个是node一个newNode if (newNode.value > node.value) { //往右走 if (node.right == null) { node.right = newNode } else { insertNode(node.right,newNode) } } else if (newNode.value < node.value) { //往左走 if (node.left == null) { node.left = newNode } else { insertNode(node.left,newNode) } } } this.insert = function (value) { var newNode = new Node(value) //newNode是新的节点 if (root == null) { //空树 root = newNode } else { insertNode(root, newNode)//我们会在这里调用这个方法,然后我们这个node 是从根节点开始的,rootroot,newNode } }
- 遍历也是从根节点开始操作,递归,遍历方法分前序遍历,中序遍历,后续遍历
遍历节点 var traverse = function(node,callback){ //这里会一直遍历直到看到node = null 然后会报错所以要判断 if(node == null)return //callback(node,value) 8 2 3 9 前序遍历 traverse(node.left,callback) //callback(node,value) 中序遍历 traverse(node.right,callback) //callback(node,value) 3 2 9 8 后续遍历 callback(node.value) } this.traverse = function (callback) { traverse(root , callback) }
查找节点
查找节点 //1,树还是空的 //2,树不是空的二叉收搜树 var min = function (node) { if (node == null) return null //在这里我们不在使用递归的方式了 while (node && node.left) { node == node.left } return node.value } this.min = function (node) { return min(root) } var max = function (node) { if (node == null) return null //在这里我们不在使用递归的方式了 while (node && node.right) { node == node.right } return node.value } this.max = function (node) { return max(root) }
删除节点:最复杂的。删除,移除节点remove(value) 第一种:移除页节点。第二种:移除只有一个子节点的节点。第三种:移除有两个子节点的情况。移除代码实例是重新构建树
//移除节点 root var findMimNode = function (node) { if (node == null) return null while (node && node.left) { node = node.left } return node //返回最小的子节点 } var rmoverNode = function (node, value) { if (node == null) return null if (value > node.value) { //继续向右查找 node.right = removeNode(node.right, value) return node } else if (value < node.value) { //继续向左查找 node.left = removeNode(node.left, value) return node } else { //都相等value == node.value //那就开始执行删除过程 if (node.left == null && node.right == null) { //满足页节点条件 node = null return node } //只有一个子节点条件 if (node.left == null && node.right) { return node.right } else if(node.right == null && node.left){ return node.left } //有两个子节点 var aux = findMimNode(node.right) //aux 查找到的子节点 node.value = aux.value node.right = removeNode(node.right, aux.value) return node } } this.remove = function (value) { root = removeNode(root, value) } var t = new Tree t.insert(8) t.insert(2) t.insert(3) t.insert(9) t.remove(3)
7,图
- 我们学的数据结构可以分两大类:第一类是比较基础的数据结构:栈、队列、数组、集合、字典等可能是计算机的低层结构。第二类是比较高级的数据结构:树和图。因为树与图都是做过我们大型的一些程序,是用这种数据结构来做的比如说html就是用树状结构来做的。那这个图结构是可以用来做地图的,另外可以用来做社交网络,所以现在我们用js去实现一个js的图结构。
- 问题:(1)如何生成一个图结构。他们之间会有一些二维的结构,连接,单项结构
- (2)怎么实现,怎么遍历怎么操作
- (3)获取图的一些路径(最短路径)
- 图概念,是一种计算机中使用广泛的结构,比如我们常用的社交网络
- 有向图
- 无向图
要理解下面的代码怎么去存储顶点,存储边,以及实现他们的链接的。
var Graph = function(){ //存储顶点 var vertices = [] //存储边 var adjList = {} //1.添加顶点 this.addVertex = function(v){ vertices.push(v) adjList[v] = [] } //2.添加边 this.addEdge = function(a,b){ adjList[a].push(b) adjList[b].push(a) } this.print = function(){ var s = "\n" for(var i = 0; i < vertices.length; i++){ var dingdian = vertices[i] s += dingdian + '=>' var bian = adjList[dingdian] for(var j = 0; j < bian.length; j++){ s += bian[j] } s += '\n' } console.log(s); } } var g = new Graph g.addVertex('A') g.addVertex('B') g.addVertex('C') g.addVertex('D') g.addVertex('E') g.addVertex('F') g.addVertex('G') g.addEdge('A','B') g.addEdge('A','C') g.addEdge('A','D') g.addEdge('B','E') g.addEdge('E','F')
理解怎么去找最短路径,他们是怎么遍历的(遍历有前,中,后)只是改变了一个callback,很神奇,改变一下回调函数的顺序就有不同的结果。跟递归栈有很大的关系。
//初始遍历将 white未发现 grey已发现 black 已探索 v='A' this.bfs = function(v,callback){ var initColor = function(){ var color = {} for(var i = 0; i < vertices.length; i++ ){ color[vertices]= 'white' } return color } this.bfs = function(v,callback){ var color = initColor() //color ={ A:'while',B:'while',C:'while'}现在的color是这样的 var queue = new queue queue.enqueue(v) //对A执行入列操作,假设v是A while(!queue.isEmpty()){ //首先就是A出列 var now = queue.dequeue() //继续取元素 var bian = adjList[now] //通过遍历for循环去获取它的边缘 for(var i = 0; i < bian.length; i++){ var w = bian[i] if(color[w] === 'white'){ //w呢是遍历到这个边缘w这个b,然后判断 //未发现的全部入列,并且标志为已发现 color[w] = 'grey' //然后将颜色标志灰色 queue.enqueue(w) } } color[now] = 'black' if(callback){ callback(now) } } } } }
找最近的路径方法特别简单,只要不停的查找回溯点就可以了
//广度优先算法 //d 距离 //pred 回溯点 //找最短路径 this.bfs = function (v, callback) { var initColor = function () { var color = {} for (var i = 0; i < vertices.length; i++) { color[vertices] = 'white' } return color } this.BFS = function (v, callback) { var color = initColor() //color ={ A:'while',B:'while',C:'while'}现在的color是这样的 var queue = new queue queue.enqueue(v) var d = {} var pred = {} for (var i = 0; i < vertices.length; i++) { d[vertices[i]] = 0 pred[vertices[i]] = null } while (!queue.isEmpty()) { //首先就是A出列 var now = queue.dequeue() var bian = adjList[now] //通过遍历for循环去获取它的边缘 for (var i = 0; i < bian.length; i++) { var w = bian[i] if (color[w] === 'white') { //w呢是遍历到这个边缘w这个b,然后判断 //未发现的全部入列,并且标志为已发现 color[w] = 'grey' //然后将颜色标志灰色 //设置回溯点 pred[w] = null //意思pred['B']='A' d[w] = d[now] + 1 queue.enqueue(w) } } color[now] = 'black' if (callback) { callback(now) } } return { pred: pred, d: d } } } //广度的优先算法对于一种能解决问题,能保证每个点的回溯点是最短的。 //添加新的路径 g.addEdge('D','F') //form....to..意思是从哪里来到哪里去 var zuiduan = function (from,to){ var v = to ;//设置当前 vhile(v !== from){ path.push(v) v = s.pred[v] } path.push(v) var str = '' while(!path.isEmpty()){ str += path.pop() + '-' } str = str.slice(0,str.length-1) console.log(str); //F点的回溯点事B } zuiduan('A','F')
在这个图结构算法,我们还剩一个深度优先算法。
很像一个数结构的,树,递归,。如果用到递归一定要用到辅助函数
总结:广度优先算法是它天然可以去处理一个最短的路径
深度优先算法是它遍历的时候可以探索到最深的一个图的最深点,相当于一个变形的树状结构(因为用了一个递归的东西)