算法


2020-2-3 JavaScript

# 栈结构

function Stack() {
	this.items = [];
	// 入栈
	Stack.prototype.push = function(element) {
		return this.items.push(element);
	};
	// 从栈中取出元素
	Stack.prototype.pop = function() {
		return this.items.pop();
	};
	// 查看栈顶元素
	Stack.prototype.peek = function() {
		return this.items[0];
	};
	// 判断栈是否为空
	Stack.prototype.isEmpty = function() {
		return this.items.length == 0;
	};
	// 获取栈中元素个数
	Stack.prototype.size = function() {
		return this.items.length;
	};

	Stack.prototype.toString = function() {
		var resultString = "";
		for (var i = 0; i < this.items.length; i++) {
			resultString += this.items[i] + " ";
		}
		return resultString;
	};
}
var stack = new Stack();

# 栈——十进制转换二进制

在现实生活中,我们主要使用十进制,但是在计算科学中,二进制非常重要,因为计算机里的所有内容都是用二进制数字表示的(0 和 1);没有十进制和二进制相互转化的能力,与计算机交流就很困难。

如何实现十进制转二进制?

要把十进制转化为二进制,我们可以将该十进制数值和 2 整除,直到结果为 0 为止

function dec2bin(decNumber) {
	// 定义栈对象
	// 循环操作
	while (decNumber > 0) {
		// 获取余数,并且放入栈中
		stack.push(decNumber % 2);
		// 获取整除后的结果,作为下一次运行的数字
		decNumber = Math.floor(decNumber / 2);
	}
	// 从栈中取出0和1
	var binaryString = "";
	while (!stack.isEmpty()) {
		binaryString += stack.pop();
	}
	return binaryString;
}

# 队列结构

function Queue() {
	this.items = [];
}
// 加入队列
Queue.prototype.enqueue = function(element) {
	return this.items.push(element);
};
// 从列表中删除前端元素
Queue.prototype.dequeue = function() {
	return this.items.shift();
};
// 查看前端元素
Queue.prototype.front = function() {
	return this.items[0];
};
Queue.prototype.isEmpty = function() {
	return this.items.length == 0;
};
Queue.prototype.size = function() {
	return this.items.length;
};
Queue.prototype.toString = function() {
	var resultString = "";
	for (var i = 0; i < this.items.length; i++) {
		resultString += this.items[i] + " ";
	}
	return resultString;
};
// 击鼓传花算法
function passGame(nameList, num) {
	// 创建一个队列结构
	var queue = new Queue();
	// 将所有人一次加入到队列中
	for (var i = 0; i < nameList.length; i++) {
		queue.enqueue(nameList[i]);
	}
	// 开始数数字
	while (queue.size() > 1) {
		// 不是num的时候,重新加入到队列的末尾
		// 是num这个数字的时候,将其从队列中删除
		// num数字之前的人重新放入到队列的末尾
		for (var j = 0; j < num - 1; j++) {
			queue.enqueue(queue.dequeue());
		}
		// num对应这个人,直接从队列中删除掉
		queue.dequeue();
	}

	// 获取剩下的那个人
	alert(queue.size());
	var endName = queue.front();
	alert("最终剩下的人:" + endName);
	return nameList.indexOf(endName);
}
names = ["a", "b", "c", "d", "f"];
alert(passGame(names, 3));

# 优先级队列

优先级队列的特点:

  • 普通的对队列插入一个元素,数据会被放在后端,并且需要前面所有的元素都处理完成后才会处理前面的数据。
  • 但是优先级队列,在出入一个元素的时候会考虑该数据的优先级。
  • 和其他数据优先级进行比较,比较完成后,可以得出这个元素在队列中正确的位置。
  • 其他处理方式,和基本队列的处理方式一样。

优先级队列主要考虑的问题:

  • 每个元素不再是一个数据,而且包含数据的优先级
  • 在添加方式中,根据优先级放入正确的位置。

# 优先级队列的实现

  • 封装元素和优先级放在一起
  • 添加元素时,将插入元素的优先级和队列中已经存在的元素优先级进行比较,已获得自己正确的位置。

优先级对垒代码实现:

function PriorityQueue() {
	// 封装一个新的构造函数,用于保存元素和元素的优先级
	function QueueElement(element, priority) {
		this.element = element;
		this.priority = priority;
	}
	// 封装属性
	this.items = [];

	// 实现插入方法
	PriorityQueue.prototype.enqueue = function(element, priority) {
		// 根基传入的元素,创建新的QueueElement
		var queueElement = new QueueElement(element, priority);
		// 获取传入元素应该在正确的位置
		if (this.items.length == 0) {
			this.items.push(queueElement);
		} else {
			var added = false;
			for (var i = 0; i < this.items.length; i++) {
				// 注意: 我们这里是数字越小,优先级越高
				if (queueElement.priority < this.items[i].priority) {
					this.items.splice(i, 0, queueElement);
					added = true;
					break;
				}
			}
			// 遍历完所有的元素,优先级都大于新插入的元素时,就插入到最后
			if (!added) {
				this.items.push(queueElement);
			}
		}
	};

	// 从列表中删除前端元素
	PriorityQueue.prototype.dequeue = function() {
		return this.items.shift();
	};
	// 查看前端元素
	PriorityQueue.prototype.front = function() {
		return this.items[0];
	};
	PriorityQueue.prototype.isEmpty = function() {
		return this.items.length == 0;
	};
	PriorityQueue.prototype.size = function() {
		return this.items.length;
	};
	PriorityQueue.prototype.toString = function() {
		var resultString = "";
		for (var i = 0; i < this.items.length; i++) {
			resultString +=
				this.items[i].element + "-" + this.items[i].priority + " ";
		}
		return resultString;
	};
}

var pq = new PriorityQueue();
pq.enqueue("a", 1);
pq.enqueue("b", 100);
pq.enqueue("c", 10);
pq.enqueue("d", 50);
alert(pq); // a-1 c-10 d-50 b-100

# 链表结构以及数组的缺点

链表和数组一样,可以用于存储一系列的元素,但是链表和数组的实现机制完全不同 数组:要存储多个元素,数组可能是最常用的数据结构,几乎每一种编程语言都有默认实现数组结构。

但是数组也有很多缺点:

  • 数组的创建通常需要申请一段连续的内存空间(一整块内存),并且大小是固定的(大多数编程语言数组都是固定的),所以当当前数组不能满足容量需求时,需要扩容。(一般情况下是申请一个更大的数组,比如 2 倍,然后将原数组的元素复制过去)
  • 而且在数组开头或中间位置插入数据的成本很高,需要进行大量元素的位移。
  • 尽管我们已经学过 JavaScript 的 Array 类方法可以帮助我们做这些事情,但背后的原理依然是这样的。

# 链表的优势

  • 要存储多个元素,另外一个选择就是链表
  • 但不同于数组,链表中的图案书在内存中不必是连续的空间。
  • 链表的每个元素有一个存储元素本身的节点和一个指向下一个元素的引用(有些语言称为指针或者连接)组成。

相对于数组,建表有一些优点:

  • 内存空间不是必须连续的,可以充分利用计算机的诶春,实现灵活的内存动态管理。
  • 链表不必在创建时就确定大小,并且大小可以无限的延伸下去。
  • 链表在插入和删除数据时,时间复杂度可以达到 O(1),相对数组效率高很多。

相对于数组,建表有一些缺点:

  • 链表访问任何一个位置的元素时,都需要从头开始访问(无法跳过第一个元素访问任何一个元素)
  • 无法通过下标直接访问元素,都需哟从头一个个访问,这道找到对应的元素。

# 链表到底是什么?

链表类似于火车:有一个火车头,火车头会连接一个节点,节点上有乘客(类似于数据),并且这个节点会连接下一个节点,以此类推。

# 链表常见操作

  • append(element) 向列表尾部添加一个新的项
  • insert(position,element) 向列表特定位置插入一个新的项
  • get(position) 获取对应位置的元素
  • indexOf(element) 返回元素在列表中的索引。如果列表中没有该元素则返回-1
  • update(position) 修改某个位置的元素
  • removeAt(position) 从列表的特定位置移除一项
  • isEmpty() 如果链表中不包含任何元素,返回 true,如果链表长度大于 0 则返回 false
  • size() 返回链表包含的元素个数,与数组的 length 属性相似
  • toString() 由于列表项使用了 Node 类,就需要重写继承值 JaVaScript 对象默认的 toString 方法,让其只输出元素的值
function LinkedList() {
	function Node(data) {
		this.data = data;
		this.next = null;
	}
	this.head = null;
	this.length = 0;
	LinkedList.prototype.append = function(data) {
		// 创建新节点
		var newNode = new Node(data);
		// 判断是否添加的是第一个节点
		if (this.length == 0) {
			this.head = newNode;
		} else {
			// 找到最后一个节点
			var current = this.head;
			while (current.next) {
				current = current.next;
			}
			// 最后节点的next指向新的节点
			current.next = newNode;
		}
		this.length += 1;
	};

	LinkedList.prototype.insert = function(position, data) {
		// 对position进行越界判断
		if (position < 0 || position > this.length) return false;

		// 根据data创建newNode
		var newNode = new Node(data);

		// 判断插入位置是否是第一个
		if (position == 0) {
			newNode.next = this.head;
			this.head = newNode;
		} else {
			var index = 0;
			var current = this.head;
			var previous = null;
			while (index++ < position) {
				previous = current;
				current = current.next;
			}
			newNode.next = current;
			previous.next = newNode;
		}
		//
		this.length += 1;
	};
	LinkedList.prototype.get = function(position) {};
	LinkedList.prototype.indexOf = function(element) {};
	LinkedList.prototype.update = function(position) {};
	LinkedList.prototype.removeAt = function(position) {};
	LinkedList.prototype.remove = function(element) {};
	LinkedList.prototype.isEmpty = function() {};
	LinkedList.prototype.size = function() {};
	LinkedList.prototype.toString = function() {
		// 定义变量
		var current = this.head;
		var listString = "";
		// 循环获取一个个的节点
		while (current) {
			listString += current.data + " ";
			current = current.next;
		}
		return listString;
	};
}
var list = new LinkedList();
list.append("a");
list.append("b");
list.append("c");
alert(list); // "a b c"
上次更新: 2/7/2020, 9:39:26 PM