数据结构
栈
栈(stack),它是一种运算受限的线性表,后进先出(LIFO);
LIFO表示就是后进入的元素, 第一个弹出栈空 间. 类似于自动餐托盘, 最后放上的托盘, 往往先把拿出去使用. 其限制是仅允许在表的一端进行插入和删除运算。这一端被称 为栈顶,相对地,把另一端称为栈底。 向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素 放到栈顶元素的上面,使之成为新的栈顶元素; 从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除 掉,使其相邻的元素成为新的栈顶元素。
栈实现
//类-栈封装
function Stack() {
// ...
// 属性
this.item = [];
// 方法-添加栈顶
Stack.prototype.push = function (element) {
return this.item.push(element);
}
// 删除栈顶-出栈
Stack.prototype.pop = function () {
return this.item.pop();
}
// 返回栈顶
Stack.prototype.peek = function () {
return this.item[this.item.length - 1];
}
// 是否为空
Stack.prototype.isEmpty = function () {
return this.item.length == 0;
}
// 清空全部栈元素 谨慎使用
Stack.prototype.clear = function () {
return this.item = [];
}
// 返回长度
Stack.prototype.size = function () {
return this.item.length;
}
}
let arr = new Array();
let stack=new Stack();
stack.push(6);
stack.push(5);
console.log(stack.pop());//5
stack.push(4);
console.log(stack.pop());//4
stack.push(3);
console.log(stack.pop());//3
console.log(stack.pop());//6
stack.push(2);
stack.push(1);
console.log("栈是否为空"+stack.isEmpty());//false
console.log("栈的长度"+stack.size());//2
console.log(stack.pop());//1
console.log(stack.pop());//2
stack.push("a")
console.log(stack.peek());//a
console.log("栈清空前长度"+stack.size());//1
stack.clear();
console.log("栈清空后长度"+stack.size());//0
stack.push("b");
console.log(stack.peek());//b
console.log("栈的长度"+stack.size())//1
es6实现栈
class Stack{
constructor(){
this.item=[];
}
//增加元素
push(el){
return this.item.push(el);
}
// 删除栈顶-出栈
pop(){
return this.item.pop();
}
// 返回栈顶
peek(){
return this.item[this.item.length - 1];
}
// 是否为空
isEmpty(){
return this.item.length == 0;
}
// 清空全部栈元素 谨慎使用
clear(){
return this.item = [];
}
// 返回长度
size(){
return this.item.length;
}
toString(){
let re="";
for (let i = 0; i < this.item.length; i++) {
const element = this.item[i];
re+=element
}
return re;
}
}
应用:
括号是否匹配
// 1.设计一个 括号是否匹配的方法 (栈)
function isTrue(str) {
let stack = new Stack();
for (let i = 0; i < str.length; i++) {
const el = str[i];
if (el == "(" || el == "[" || el == "{") {
stack.push(el)
} else {
switch (el) {
case ")":
if (stack.peek() != "(") {
return false;
}
stack.pop();
break;
case "]":
if (stack.peek() != "[") {
return false;
}
stack.pop();
break;
case "}":
if (stack.peek() != "{") {
return false;
}
stack.pop();
break;
default:
break;
}
}
}
if (stack.isEmpty()) {
return true;
}
return false;
}
console.log(isTrue("(){}[]"));//true
console.log(isTrue("{[()]}"));//true
console.log(isTrue("{[(]}"));//false
判断回文数
// 2. 判断当前字符串是不是回文数 (栈)
//回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数
function Backward(data) {
let back = ""
let stack = new Stack()
for (let i = 0; i < data.length; i++) {
stack.push(data[i])
}
while (stack.size() > 0) {
back += stack.pop();
}
if (back == data) {
return true
} else {
return false;
}
}
console.log(Backward("aba"));
console.log(Backward("abaa"));
console.log(Backward("121"));
console.log(Backward("1221"));
十转任意进制
// 3. 进制转换(十转任意进制)(栈)
function transDecimal(el, base) {
el = Math.floor(el)
base = Math.floor(base) || 2;
let result = "";
let stack = new Stack();
let code = "0123456789ABCDEF";
while (el > 0) {
let remainder = el % base;
stack.push(remainder);
el = Math.floor(el / base)
}
while (stack.size() > 0) {
result += code[stack.pop()]
}
while(result==0){
return 0;
}
return result;
}
console.log(transDecimal(10, 2));//1010
console.log(transDecimal(10, 8));//12
console.log(transDecimal(10, 16));//A
console.log(transDecimal(3015, 16));//BC7
console.log(transDecimal(0, 16));//0
console.log(transDecimal(0, 2));//0
佩兹糖果盒
//4.佩兹糖果盒
var sweetBox = new Stack();
sweetBox.push('red');
sweetBox.push('yellow');
sweetBox.push('red');
sweetBox.push('yellow');
sweetBox.push('white');
sweetBox.push('yellow');
sweetBox.push('white');
sweetBox.push('yellow');
sweetBox.push('white');
sweetBox.push('red');
// console.log(sweetBox);
function getsuger(el,stack){
let getsugerStack=new Stack();
let setsugerStack=new Stack();
while(stack.size()>0){
if(stack.peek()==el){
getsugerStack.push(el);
stack.pop();
}else{
setsugerStack.push(stack.peek());
stack.pop()
}
}
while(setsugerStack.size()>0){
stack.push(setsugerStack.peek());
setsugerStack.pop()
}
return stack;
}
console.log(getsuger("yellow",sweetBox));
队列
普通队列
class Queue {
constructor() {
this.item = [];
}
enqueue(element) {
this.item.push(element)
}
dequeue() {
return this.item.shift();
}
front() {
return this.item[0];
}
isEmpty() {
return this.item.length == 0;
}
size() {
return this.item.length;
}
}
let queue = new Queue();
queue.enqueue("abc")
queue.enqueue("acd")
queue.enqueue("12c")
queue.enqueue("nbc")
console.log(queue.front());//abc
console.log(queue.dequeue());//abc
console.log(queue.front());//acd
console.log(queue.isEmpty()); // false
console.log(queue.size()); // 3
优先级队列
class Priorityel {
constructor(el, priority) {
this.el = el;
this.priority = priority
}
}
class PriorityQueue {
constructor() {
this.item = [];
}
enqueue(el, priority) {
// 创建
let data = new Priorityel(el, priority)
if (this.isEmpty()) {
this.item.push(data);
} else {
let added = false;
for (let i = 0; i < this.item.length; i++) {
if (data.priority < this.item[i].priority) {
this.item.splice(i, 0, data);
added = true;
break;
}
}
if (!added) {
this.item.push(data);
}
}
}
toString() {
let result = ''
for (let i = 0; i < this.item.length; i++) {
const data = this.item[i];
result += data.el + "-" + data.priority + ' '
}
return result;
}
dequeue() {
return this.item.shift();
}
front() {
return this.item[0];
}
isEmpty() {
return this.item.length == 0;
}
size() {
return this.item.length;
}
}
应用:
约瑟夫环
function josephRing(sum,number){
var queue=new Queue();
for(let i=1;i<=sum;i++){
queue.enqueue(i)
}
while(queue.size()>2){
for(let i=0;i<number-1;i++){
queue.enqueue(queue.dequeue())
}
queue.dequeue();
}
console.log("在第"+queue.dequeue()+"位安全");
console.log("在第"+queue.dequeue()+"位安全");
}
josephRing(41,3)
//16 32
击鼓传花
//引入队列js
function Jigu(list,number) {
// 所有人入队
let queue = new Queue();
list.forEach((item) => {
queue.enqueue(item);
});
while (queue.size() > 1) {
// let number = Math.floor(Math.random() * 10) ;
// 安全 number-1
for (let i = 0; i < number - 1; i++) {
// let temp = queue.dequeue();
// queue.enqueue(temp);
queue.enqueue(queue.dequeue());
}
// number 危险
queue.dequeue();
}
alert("胜利者: " + queue.front());
alert("在队列中的位置: " + list.indexOf(queue.dequeue()));
}
let list = ["Tom", "Lily", "Lilei", "Rose", "Jake", "Join"]
// 胜利者的名字 在原队伍的位置
Jigu(list,3);
链表
单向链表
//链表封装
class Node {
constructor(el) {
this.data = el;
this.next = null;
}
}
class linkList {
constructor() {
this.head = null;
this.length = 0;
}
//以下几种方法
}
//添加 append向列表尾部添加一个新的项
append(el) {
let newNode = new Node(el)
//头部为空 新元素
if (this.head == null) {
this.head = newNode;
} else {
//current当前
//不为空
let current = this.head;
//如果next指向null 不后移
//反之 后移
while (current.next) {
current = current.next;
}
current.next = newNode;
}
this.length++;
}
// insert(position, el):向列表的特定位置插入一个新的项。
insert(el, position) {
//position是否合法
if (position < 0 || position > this.length || !Number.isInteger(position)) {
return false;
}
let newNode = new Node(el)
//插入位置在最开始
if (position == 0) {
//如果头部没有东西,插入元素作为head
if (this.head == null) {
this.head = newNode;
}
//顶部有东西,将顶部的放到next,新加入元素作为新head
else {
newNode.next = this.head;
this.head = newNode
}
}
//插入位置在末尾 直接添加
else if (position == this.length) {
this.append(el)
} else {
//方法一:
let current = this.head;
let previous = null;
let index = 0;
while (index < position) {
previous = current;
current = current.next;
index++
}
newNode.next = current;
previous.next = nextNode;
this.length++
//方法二:
// let current=this.head,index=0;
// while (index++<position-1) {
// current=current.next
// }
// newNode.next=current;
// previous.next=nextNode;
// this.length++
}
return list;
}
//toString():由于列表项使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其只输出元素的值。
toString() {
let listData = "";
let current = this.head;
while (current) {
listData += "-" + current.data;
current = current.next
}
//截取
return listData.slice(1)
}
//indexOf(el):返回元素在列表中的索引。如果列表中没有该元素则返回-1。
indexOf(el) {
let current = this.head, index = 0;
while (current) {
if (current.data === el) {
return index;
}
current=current.next;
index++
}
return -1;
}
//removeAt(position):从列表的特定位置移除一项
removeAt(position) {
//Number.isInteger 是否是整数 是返回true
if (position < 0 || position > this.length - 1 || !Number.isInteger(position)) {
return false
}
let current = this.head, index = 0;
if (position == 0) {
this.head = current.next
}
//移除其他
else {
while (index++ < position - 1) {
current = current.next
}
current.next = current.next.next
}
this.length--;
return list;
// return true;
}
//remove(el):从列表中移除一项。
remove(el){
return this.removeAt(this.indexOf(el));
}
//isEmpty():是否为空
isEmpty(){
return this.length==0;
}
//翻转
flipList(){
let prev=null;
let current=this.head;
while (current) {
let temp=current.next;
current.next=prev;
prev=current;
current=temp;
}
this.head=prev;
return this;
}
//两两交换
exchange(){
//传一个没有意义的数
let newNode=new Node("head")
newNode.next=this.head;
let prev=newNode;
while (prev.next && prev.next.next) {
let temp=prev.next;
let buffer=prev.next.next;
prev.next=buffer;
temp.next=buffer.next;
buffer.next=temp;
prev=temp;
}
this.head=newNode.next;
return this;
}
//使用
let list = new linkList();
for (let i = 0; i < 10; i++) {
list.append(i)
}
console.log(list.insert(55, 0));
console.log(list.insert(10, 10));
console.log(list.insert(10, 11));
console.log(list.toString());
console.log(list.length);
console.log(list.indexOf(6));
console.log(list.removeAt(2));
console.log(list.isEmpty());
单向链表实现队列
//引入链表js
class QueueList {
constructor() {
this.items=new linkList()
}
enqueue(el) {
this.items.append(el)
}
pop(el){
this.items.removeAt(el)
}
toString() {
this.items.toString();
}
clear(){
this.front=this.rear=null;
this.length=0;
return true;
}
}
let mylist = new QueueList();
for (let i = 0; i < 10; i++) {
mylist.enqueue(i)
}
console.log(mylist.toString());
mylist.enqueue(3);
console.log(mylist.toString());
双向链表
单向链表: 只能从头遍历到尾 也就是链表相连的过程是单向的. 实现的原理是上一个链表中 有一个指向下一个的引用. 单向链表有一个比较明显的缺点: 我们可以轻松的到达下一个节点, 但是回到前一个节点是很 难的. 但是, 在实际开发中, 经常会遇到需要回到上一个节点
双向链表 既可以从头遍历到尾, 又可以从尾遍历到头 也就是链表相连的过程是双向的. 那么它的实现原理, 你能猜到 吗? 一个节点既有向前连接的引用, 也有一个向后连接的引用. 双向链表可以有效的解决单向链表中提到的问题. 双向链表有什么缺点呢? 每次在插入或删除某个节点时, 需要处理四个节点的引用, 而不是两个. 也就是实现起来要困难一些 并且相当于单向链表, 必然占用内存空间更大一些. 但是这些缺点和我们使用起来的方便程度相比, 是微不足道 的.
![](data-img/链表2.png)
class DoubleNode {
constructor(el) {
this.prev = null;
this.data = el;
this.next = null;
}
}
class DoubleList {
constructor() {
this.head = null;
this.tail = null;
this.length = 0;
}
//添加节点
append(el) {
let newNode = new DoubleNode(el);
if (this.head == null) {
this.head = newNode;
this.tail = newNode
} else {
this.tail.next = newNode;
newNode.prev = this.tail;
this.tail = newNode
}
this.length++
}
//双向链表正向遍历
ForwardString() {
let result = "";
let current = this.head;
while (current) {
result += "-" + current.data;
current = current.next;
}
return result.slice(1);
}
//双向链表反向遍历
ReverseString() {
let result = "";
let current = this.tail;
while (current) {
result += "-" + current.data;
current = current.prev;
}
return result.slice(1);
}
//插入节点
insert(el, position) {
if (position < 0 || position > this.length || !Number.isInteger(position)) {
return false;
}
let newNode = new DoubleNode(el)
if (position == 0) {
if (this.head == null) {
this.head = newNode;
this.tail = newNode
} else {
newNode.next = this.head;
this.head.prev = newNode;
this.head = newNode
}
} else if (position == this.length) {
this.append(el)
} else {
// let newNode=new DoubleNode(el)
let current = this.head, index = 0;
while (index++ < position) {
current = current.next;
}
newNode.next = current;
newNode.prev = current.prev;
current.prev.next = newNode;
current.prev = newNode;
this.length++
}
}
//查找元素
indexOf(el) {
let current = this.head, index = 0;
while (current) {
if (current.data === el) {
return index;
}
index++;
current = current.next;
}
return -1;
}
//删除特定位
removeAt(position) {
//Number.isInteger 是否是整数 是返回true
if (position < 0 || position > this.length - 1 || !Number.isInteger(position)) {
return false
}
if (position == 0) {
if (this.length == 1) {
this.head = null;
this.tail = null;
} else {
this.head = this.head.next;
this.head.prev = null;
}
} else if (position == this.length - 1) {
this.tail = this.tail.prev
this.tail.next = null
} else {
let current = this.head, index = 0;
while (index++ < position) {
current = current.next
}
current.prev.next = current.next;
current.next.prev = current.prev
}
this.length--;
return true;
}
//remove(el):从列表中移除一项。
remove(el) {
return this.removeAt(this.indexOf(el));
}
//isEmpty():是否为空
isEmpty() {
return this.length == 0;
}
}
//使用
let doublelist=new DoubleList();
for (let i = 0; i < 10; i++) {
doublelist.append(i)
}
console.log(doublelist)
console.log(doublelist.ForwardString())//0
console.log(doublelist.ReverseString());//9-8-7-6-5-4-3-2-1-0
doublelist.insert(22,0);
console.log(doublelist.ForwardString())//22-0
console.log(doublelist.ReverseString());//9-8-7-6-5-4-3-2-1-0-22
console.log(doublelist)
console.log(doublelist.indexOf(3));
doublelist.removeAt(2)
console.log(doublelist)
单向循环链表
class Node {
constructor(el) {
this.data = el;
this.next = null;
}
}
class CycleList {
constructor() {
this.head = null;
this.length = 0;
}
//添加 append向列表尾部添加一个新的项
append(el) {
let newNode = new Node(el)
//头部为空 新元素
if (this.head == null) {
//指向自己
this.head = newNode;
newNode.next = newNode
} else {
let current = this.head
//判断current是否为尾节点 不是->继续后移
while (current.next != this.head) {
current = current.next
}
newNode.next = this.head;
current.next = newNode;
}
this.length++ toString() {
let result = "";
let index = 0;
let current = this.head;
//循环结束条件
//如果用current.next != this.head做while判定
//开头要加上this.head==null的情况
//循环结束后要加上尾部 current.next=newNode
while (index++ < this.length) {
result += "-" + current.data;
current = current.next
}
return result.slice(1)
//插入
insert(el, position) {
if (position < 0 || position > this.length || !Number.isInteger(position)) { return false;
}
let newNode = new Node(el)
//插入位置在最开始
if (position == 0) {
//是否是空链表
if (this.head == null) {
this.head = newNode;
newNode.next = this.head
}
else {
//找尾节点
let current = this.head
//判断current是否为尾节点 不是->继续后移
while (current.next != this.head) {
current = current.next
}
newNode.next = this.head;
current.next = newNode;
this.head = newNode
}
this.length++
} else if (position == this.length) {
this.append(el)
} else {
let current = this.head, index = 0;
while (index++ < position - 1) {
current = current.next
}
newNode.next = current.next;
current.next = newNode;
this.length++
}
}
indexOf(el) {
let current = this.head, index = 0;
while (index < this.length) {
if (current.data === el) {
return index;
}
index++
current = current.next;
}
return -1;
}
removeAt(position) {
if (position < 0 || position > this.length - 1 || !Number.isInteger(position)) {
return false;
}
if (position == 0) {
if (this.length == 1) {
this.head == null;
} else {
let current = this.head
//判断current是否为尾节点 不是->继续后移
while (current.next != this.head) {
current = current.next
}
this.head = this.head.next;
current.next = this.head
}
} else{
let current = this.head, index = 0;
while (index++ < position) {
current = current.next;
}
current.next=current.next.next
}
this.length--
}
remove(el){
return this.removeAt(this.indexOf(el))
}
isEmpty(){
return this.length==0;
}
}
约瑟夫环
function josephRing(personlist,kill,winner){
var list=new DoblecycleList();
personlist.forEach(items => {
list.append(items)
});
let count=1;
let current=list.head;
while(list.length>winner){
if(count==kill){
list.remove(current.data)
count=1;
current=current.next;
}else{
current=current.next;
count++;
}
}
let newlist=[];
for (let i = 0; i < personlist.length; i++) {
newlist.push(i)
}
return newlist.toString();
}
console.log(josephRing([41,3,2]) );
双向循环链表
class DoubleNode {
constructor(el) {
this.prev = null;
this.data = el;
this.next = null;
}
}
class DoblecycleList {
constructor() {
this.head = null;
this.tail = null;
this.length = 0;
}
append(el) {
let newNode = new DoubleNode(el)
//头部为空 新元素
if (this.head == null) {
this.head = newNode;
this.tail = newNode;
newNode.next = newNode
newNode.prev = newNode
} else {
let current = this.head
//判断current是否为尾节点 不是->继续后移
while (current.next != this.head) {
current = current.next
}
newNode.next = this.head;
newNode.prev = this.tail;
this.head.prev = newNode;
this.tail.next = newNode;
this.tail = newNode;
current.next = newNode;
}
this.length++;
}
ForwardString() {
let result = "";
let current = this.head;
let index = 0
while (index++ < this.length) {
result += "-" + current.data;
current = current.next
}
//截取
return result.slice(1)}
ReverseString() {
let result = "";
let index = this.length - 1;
let current = this.tail;
while (index-- >= 0) {
result += "-" + current.data;
current = current.prev;
}
return result.slice(1);}
insert(el, position) {
if (position < 0 || position > this.length || !Number.isInteger(position)) {
return false;
}
let newNode = new DoubleNode(el)
if (position == 0) {
if (this.head == null) {
this.head = newNode;
this.tail = newNode;
newNode.next = this.head;
newNode.prev = this.tail
} else {
newNode.next = this.head;
newNode.prev = this.tail;
this.head.prev = newNode;
this.head.tail = newNode;
this.head = newNode;
}
this.length++
} else if (position == this.length) {
this.append(el)
} else {
let current = this.head, index = 0;
while (index++ < position - 1) {
current = current.next;
}
newNode.next = current.next;
newNode.prev = current;
current.next.prev = newNode;
current.next = newNode;
this.length++
}
}
removeAt(position) {
if (position < 0 || position > this.length - 1 || !Number.isInteger(position)) {
return false
}
if (position == 0) {
if (this.length == 1) {
this.head = null;
this.tail = null;
} else {
this.head = this.head.next;
this.head.prev = this.tail;
this.tail.nexe=this.head
}
} else if (position == this.length - 1) {
this.tail = this.tail.prev
this.tail.next = this.head;
this.head.prev=this.tail;
} else {
let current = this.head, index = 0;
while (index++ < position) {
current = current.next
}
current.prev.next = current.next;
current.next.prev = current.prev
}
this.length--;
return true;
}
indexOf(el){
let current = this.head, index = 0;
while(index<this.length){
if (current.data===el) {
return index
}
current=current.next;
index++
}
return -1;
}
//remove(el):从列表中移除一项。
remove(el) {
return this.removeAt(this.indexOf(el));
}
isEmpty() {
return this.length == 0
}
}
集合
class Set {
constructor() {
this.item = {};
}
add(ele) {
// 特点: 元素不重复
if (!this.has(ele)) {
this.item[ele] = ele;
return true;
}
return false;
}
delete(ele) {
if (this.has(ele)) {
return delete this.item[ele];
}
return false;
}
has(ele) {
return this.item.hasOwnProperty(ele)
}
clear() {
this.item = {};
return true;
}
size() {
return this.setValues().length;
}
setValues() {
return Object.keys(this.item);
}
}
//并集
getUnionSet(set2){
let unionSet = new Set();
this.setValues().forEach((value)=>{
unionSet.add(value);
})
set2.setValues().forEach((value)=>{
unionSet.add(value);
})
return unionSet
}
//交集
function intersection(set1,set2){
let intersectionSet = new Set();
set1.setValues().forEach(value => {
if(set2.has(value)){
intersectionSet.add(value);
}
});
return intersectionSet;
}
let set1 = new Set();
set1.add("1")
set1.add("2")
set1.add("3")
set1.add("4")
let set2 = new Set();
set2.add("1")
set2.add("2")
set2.add("3")
set2.add("4")
set2.add("5")
let intersectionSet = intersection(set1,set2)
console.log("交集:"+intersectionSet.setValues().join(","));
//差集
function difference(set1,set2){
let differenceSet = new Set();
set1.setValues().forEach(value => {
if(!set2.has(value)){
differenceSet.add(value);
}
});
return differenceSet;
}
let set1 = new Set();
set1.add("1")
set1.add("2")
set1.add("3")
set1.add("4")
let set2 = new Set();
set2.add("1")
set2.add("2")
set2.add("3")
set2.add("4")
set2.add("5")
set2.add("6")
set2.add("7")
let differenceSet = difference(set2,set1)
console.log("差集:"+differenceSet.setValues().join(","));
//子集
function subset(set1, set2) {
let result = true;
set1.setValues().forEach(value => {
if (!set2.has(value)) {
result = false;
}
});
return result;
}
let set1 = new Set();
set1.add("1")
set1.add("2")
set1.add("3")
set1.add("4")
let set2 = new Set();
set2.add("1")
set2.add("2")
set2.add("3")
set2.add("4")
set2.add("5")
let sonSet = subset(set1, set2)
console.log("set1是set2的子集吗?" + sonSet);
字典
class Dictionary{
constructor(){
this.item={}
}
set(key,val){
this.item[key]=val;
}
remove(key){
if(this.has(key)){
delete this.item[key]
return true
}
return false
}
has(key){
return this.item.hasOwnProperty(key)
}
get(key){
return this.item[key]
}
clear(){
this.item={}
}
size(){
return this.keys().length;
}
keys(){
return Object.keys(this.item)
}
values(){
return Object.values(this.item)
}
}
//使用
let mydir=new Dictionary();
for (let i = 0; i < 26; i++) {
mydir.set(String.fromCharCode(i+65),i+1)
}
console.log(mydir.keys());
console.log(mydir.values());
console.log(mydir.size()); //26
console.log(mydir.has("A")); //true
console.log(mydir.remove("C")); //true
console.log(mydir.get("F")); //6
console.log(mydir.keys());
树
树结构和数组/链表/哈希表的对比有什么优点呢?
数组: 优点: 数组的主要优点是根据下标值访问效率会很高. 但是如果我们希望根据元素来查找对应的位置呢? 比较好的方式是先对数组进行排序, 再进行二分查找.
缺点: 需要先对数组进行排序, 生成有序数组, 才能提高查找效率. 另外数组在插入和删除数据时, 需要有大量的位移操作(插入 到首位或者中间位置的时候), 效率很低.
链表: 优点: 链表的插入和删除操作效率都很高.
缺点: 查找效率很低, 需要从头开始依次访问链表中的每个数据项, 直到找到. 而且即使插入和删除操作效率很高, 但是如果要插入和删除 中间位置的数据, 还是需要重头先找到对应的数据.
树结构: 我们不能说树结构比其他结构都要好, 因为每种数据结构都有 自己特定的应用场景. 但是树确实也综合了上面的数据结构的优点(当然优点不足于 盖过其他数据结构), 并且也弥补了上面数据结构的缺点. 而且为了模拟某些场景, 我们使用树结构会更加方便. 比如文件 的目录结构.
树结构
树的术语:
- 结点的度(Degree):结点的子树个数.
- 树的度:树的所有结点中最大的度数.
- 叶结点(Leaf):度为0的结点. (也称为叶子结点)
- 父结点(Parent):有子树的结点是其子树的根结点的父结 点
- 子结点(Child):若A结点是B结点的父结点,则称B结点是 A结点的子结点;子结点也称孩子结点。
- 兄弟结点(Sibling):具有同一父结点的各结点彼此是兄弟 结点。
- 路径和路径长度:从结点n1到nk的路径为一个结点序列n1 , n2,… , nk, ni是 ni+1的父结点。路径所包含边的个数为路径的 长度。
- 结点的层次(Level):规定根结点在1层,其它任一结点的 层数是其父结点的层数加1。
- 树的深度(Depth):树中所有结点中的最大层次是这棵树 的深度。
二叉树
二叉树的定义: 二叉树可以为空, 也就是没有结点. 若不为空,则它是由根结点和称为其左子树TL和右子树TR的 两个不相交的二叉树组成。 二叉树有五种形态: 注意c和d是不同的二叉树, 因为二叉树是有左右之分的.
二叉树的特性: 二叉树有几个比较重要的特性, 在笔试题中比较常见:
- 一个二叉树第 i 层的最大结点数为:2^(i-1), i >= 1;
- 深度为k的二叉树有最大结点总数为: 2^k - 1, k >= 1;
- 对任何非空二叉树 T,若n0表示叶结点的个数、n2是度为2的 非叶结点个数,那么两者满足关系n0 = n2 + 1。
特殊的二叉树:
完美二叉树(Perfect Binary Tree) , 也称为满二叉树(Full Binary Tree) 在二叉树中, 除了最下一层的叶结点外, 每层节点都有2个子结 点, 就构成了满二叉树.
完全二叉树(Complete Binary Tree) 除二叉树最后一层外, 其他各层的节点数都达到最大个数. 且最后一层从左向右的叶结点连续存在, 只缺右侧若干节点. 完美二叉树是特殊的完全二叉树. 下面不是完全二叉树, 因为D节点还没有右结点, 但是E节点就有 了左右节点.
二叉搜索树
二叉搜索树的特点:
二叉搜索树的特点就是相对较小的值总是保存在左结点上, 相 对较大的值总是保存在右结点上.
创建二叉树
class Node {
constructor(el) {
this.data = el;
this.left = null;
this.right = null
}
}
class BinaryTree {
constructor() {
this.root = null
}
insert(el) {
let newNode = new Node(el);
if (this.root == null) {
this.root = newNode;
} else {
//入口
insertAssist(this.root, newNode)
}
}
//先序遍历
preorderTraversal(callBack){
preorderTraversalAssist(this.root,callBack)
}
//中序遍历
inorderTraversal(callBack){
inorderTraversalAssist(this.root,callBack)
}
postorderTraversal(callBack){
postorderTraversalAssist(this.root,callBack)
}
getMin() {
let current = this.root;
if (current == null) {
return false;
}
while (current.left) {
current = current.left
}
return current.data
}
getMax() {
let current = this.root;
if (current == null) {
return false;
}
while (current.right) {
current = current.right
}
return current.data
}
//搜索1
search(key) {
return searchAssist(this.root, key)
}
//搜索2
searchWhile(key) {
if (this.root === null) {
return false;
}
let current = this.root;
while (current) {
if (current.data == key) {
return true
} else if (current.data > key) {
current = current.left
} else {
current = current.root
}
}
return false;
}
//删除
remove(el) {
if(this.root==null){
return false
}
//找到el
let current = this.root;
let parentNode = null;
let isLeftChild = true;
while (current.data != el) {
parentNode = current;
if (current == null) {
return false
}
if (current.data > el) {
current = current.left;
isLeftChild = true
} else {
current = current.right;
isLeftChild = false
}
}
let delNode=current;
//删除叶子结点
if (delNode.left == null && delNode.right == null) {
//只有一个根节点
if (delNode == this.root) {
this.root = null
}
//被删除节点是parent的左子节点
else if (isLeftChild) {
parentNode.left = null
} else {
parentNode.right = null
}
}
//只有一个左子树
else if (delNode.right == null) {
//如果是根节点 要把根给唯一继承人
if (delNode == this.root) {
this.root = current.left
} else if (isLeftChild) {
parentNode.left = delNode.left
} else {
parentNode.right = delNode.left
}
}
//只有一个右子树
else if (delNode.left == null) {
if (delNode == this.root) {
this.root = delNode.right
} else if (isLeftChild) {
parentNode.left = delNode.right
} else {
parentNode.right = delNode.right
}
} else {
//有两个子节点的节点
//1.找到后继节点
//2.让后继节点代替delNode parent ->successor
//3.successor->delNode
let successor = getSuccessor(delNode);
if (delNode == this.root) {
this.root = successor
} else if (isLeftChild) {
parentNode.left = successor;
} else {
parentNode.right = successor;
}
successor.left = delNode.left;
}
return true;
}
}
//insert的辅助函数
//将node与newNode比较
function insertAssist(node, newNode) {
if (newNode.data<node.data) {
if (node.left===null) {
node.left=newNode
// console.log(`${newNode.data}插入${node.data}左边`);
}else{
insertAssist(node.left, newNode)
}
}else{
if (node.right===null) {
node.right=newNode
}else{
insertAssist(node.right, newNode)
}
}
}
function preorderTraversalAssist(node,callBack){
if(node !=null){
//访问根节点
callBack(node.data);
//先序遍历左子树
preorderTraversalAssist(node.left,callBack)
//再遍历右边
preorderTraversalAssist(node.right,callBack)
}
}
function inorderTraversalAssist(node,callBack){
if(node !=null){
//先序遍历左子树
inorderTraversalAssist(node.left,callBack)
//访问根节点
callBack(node.data);
//再遍历右边
inorderTraversalAssist(node.right,callBack)
}
}
function postorderTraversalAssist(node,callBack){
if(node !=null){
//先序遍历左子树
postorderTraversalAssist(node.left,callBack)
//再遍历右边
postorderTraversalAssist(node.right,callBack)
//访问根节点
callBack(node.data);
}
}
function searchAssist(node,key){
if(node===null){
console.log(`没找到返回`);
return false
}
if(node.data>key){
console.log(`此时${node.data}>${key}`);
return searchAssist(node.left,key)
}else if(node.data<key){
console.log(`此时${node.data}<${key}`);
return searchAssist(node.right,key)
}else{
console.log(`此时找到${key}后返回了`);
return true;
}
}
function searchAssist(node, key) {
if (node === null) {
console.log(`没找到返回`);
return false
}
if (node.data > key) {
console.log(`此时${node.data}>${key}`);
return searchAssist(node.left, key)
} else if (node.data < key) {
console.log(`此时${node.data}<${key}`);
return searchAssist(node.right, key)
} else {
console.log(`此时找到${key}后返回了`);
return true;
}
}
function getSuccessor(delNode) {
let curr = delNode.right;
let successorNode = null;
let successorParentNode = null;
while (curr) {
successorParentNode = successorNode
successorNode = curr;
curr = curr.left;
}
if (successorNode != delNode.right) {
successorParentNode.left = successorNode.right;
successorNode.right = delNode.right;
}
return successorNode
}
//使用
let bst = new BinaryTree();
bst.insert(11)
bst.insert(7)
bst.insert(15)
bst.insert(5)
bst.insert(3)
bst.insert(9)
bst.insert(8)
bst.insert(10)
bst.insert(13)
bst.insert(12)
bst.insert(14)
bst.insert(20)
bst.insert(18)
bst.insert(25)
console.log(bst);
let re=""
bst.preorderTraversal(function(data){
re+="-"+data
})
console.log(re.slice(1));
//11-7-5-3-9-8-10-15-13-12-14-20-18-25
let re2=""
bst.inorderTraversal(function(data){
re2+="-"+data
})
console.log(re2.slice(1));
//3-5-7-8-9-10-11-12-13-14-15-18-20-25
let re3=""
bst.postorderTraversal(functi{
re3+="-"+data
})
console.log(re3.slice(1));
//3-5-8-10-9-7-12-14-13-18-25-20-15-11
先序遍历
中序遍历
后序遍历
AVL
LL
RR
LR
RL
算数排序
排序算法时间复杂度
冒泡排序
选择排序
插入排序
快速排序
实现排序
class SortAlgorithm {
constructor() {
this.item = [];
}
toString() {
return console.log(this.item.join("-"));
}
add(el) {
this.item.push(el)
}
//冒泡排序
bubbleSort() {
for (let i = this.item.length - 1; i > 0; i--) {
for (let j = 0; j < i; j++) {
if (this.item[j] > this.item[j + 1]) {
//交换两个数
this.swap(j,j+1)
}
}
}
this.toString()
}
swap(m, n) {
let temp = this.item[m]
this.item[m] = this.item[n];
this.item[n] = temp
}
//选择排序
selectionSort(){
for (let i =0; i <this.item.length - 1; i++) {
let min=i;
for (let j = min+1; j < this.item.length; j++) {
if (this.item[j] < this.item[min]) {
min=j
}
}
//变化了才移动
if (min!=i) {
this.swap(i,min)
}
}
this.toString()
}
//插入排序
insertSort(){
for (let i =1; i <this.item.length; i++) {
let mark=this.item[i];
let j=i;
while(this.item[j-1]>mark && j>0){
this.item[j]=this.item[j-1];
j--;
}
this.item[j]=mark;
}
this.toString()
}
//归并排序
mergeSort(){
this.item=divide(this.item)
this.toString()
}
// 快速排序
quickSort(){
this.item=quickAssist(this.item)
this.toString()
}
}
function quickAssist(arr){
let len=arr.length;
if (len>1) {
//找到基准 并将它剪切
let basicIndex=Math.floor(len/2)
let basic=arr.splice(basicIndex,1)[0];
let left=[];
let right=[];
for (let i = 0; i < arr.length; i++) {
//小于放左边
if (arr[i]<basic) {
left.push(arr[i])
}else{
right.push(arr[i])
}
}
//将排好的left right连接
return quickAssist(left).concat(basic,quickAssist(right))
}
return arr
}
//得到分开的数组
function divide(arr){
let len=arr.length
if (len>1) {
let index=Math.floor(len/2);
let leftArr=arr.slice(0,index);
let rightArr=arr.slice(index);
return merge(divide(leftArr),divide(rightArr));
}
return arr;
}
//合并数组
function merge(leftArr,rightArr){
let newArr=[];
while(leftArr.length && rightArr.length){
if (leftArr[0]<rightArr[0]) {
newArr.push(leftArr.shift())
}else{
newArr.push(rightArr.shift())
}
}
return newArr.concat(leftArr,rightArr)
}
//使用
let arr = [2, 34, 23, 45, 16, 78, 24, 33, 46, 90, 1]
let sort = new SortAlgorithm()
for (let i = 0; i < arr.length; i++) {
sort.add(arr[i]);
}
//原数组
sort.toString()
sort.bubbleSort()
sort.selectionSort()
sort.insertSort()
sort.mergeSort()
sort.quickSort()