浅谈数据结构

数据结构

栈(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());

双向链表

单向链表: 只能从头遍历到尾 也就是链表相连的过程是单向的. 实现的原理是上一个链表中 有一个指向下一个的引用. 单向链表有一个比较明显的缺点: 我们可以轻松的到达下一个节点, 但是回到前一个节点是很 难的. 但是, 在实际开发中, 经常会遇到需要回到上一个节点

双向链表 既可以从头遍历到尾, 又可以从尾遍历到头 也就是链表相连的过程是双向的. 那么它的实现原理, 你能猜到 吗? 一个节点既有向前连接的引用, 也有一个向后连接的引用. 双向链表可以有效的解决单向链表中提到的问题. 双向链表有什么缺点呢? 每次在插入或删除某个节点时, 需要处理四个节点的引用, 而不是两个. 也就是实现起来要困难一些 并且相当于单向链表, 必然占用内存空间更大一些. 但是这些缺点和我们使用起来的方便程度相比, 是微不足道 的.

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()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值