首先分享一下个人经历,本人是2021届毕业生,普通本科非计算机专业历经春招毒打成功上岸某大厂,已入职。互联网大厂校招算法是必备的,基础的算法不过关笔试都过不了,我在校招之前在leetcode刷了100+算法题,整理了一些自己的笔记,分享给有需要的人。个人觉得数据结构以及算法学习还是挺重要,尤其像我这种非科班出生的程序员,内容不是很完善,大家也可以留言区补充。
数据结构
1、栈
1.1 栈的概述
栈(stack),它是一种受限的线性质,后进先出(LIFO)
-
其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底
1.2 栈的常规操作
方法 | 作用 |
---|---|
push(e) | 添加一个新元素到栈顶 |
pop() | 移除栈顶元素,同时返回被移除的元素 |
peek() | 返回栈顶元素,不对栈做任何的修改 |
isEmpty() | 如果栈里没有任何元素就返回true,否则返回false |
size() | 返回栈里的元素个数。这个方法和数组的length很类似 |
toString() | 将栈结构的内容以字符串形式返回 |
1.3 用js封装栈
function Stack(){
this.stack = []
}
Stack.prototype.push=function(element) {
this.stack.push(element)
}
Stack.prototype.pop=function(){
return this.stack.pop()
}
Stack.prototype.size=function(){
return this.stack.length
}
Stack.prototype.peek=function(){
return this.stack[this.size()-1]
}
Stack.prototype.isEmpty=function() {
return this.size === 0 ? true : false
}
Stack.prototype.toString=function(){
return this.stack.join(' ')
}
1.4 栈的应用
2、队列
2.1 队列的概述
队列(queue),它是一种受限的线性质,先进先出(LIFO)
- 其限制是仅允许在表的前端进行删除操作,在表的后端进行插入操作
2.2 队列的常规操作
方法 | 作用 |
---|---|
enqueue(e) | 向队列尾部添加一个或多个新的元素 |
dequeue() | 移除队列第一个元素,同时返回被移除的元素 |
front() | 返回队列第一个元素,不对队列任何的修改 |
isEmpty() | 如果队列里没有任何元素就返回true,否则返回false |
size() | 返回队列里的元素个数。这个方法和数组的length很类似 |
toString() | 将队列的内容以字符串形式返回 |
2.3 用js封装队列
function Queue(){
this.queue = []
}
Queue.prototype.enqueue = function(ele){
this.queue.push(ele)
}
Queue.prototype.dequeue = function(){
return this.queue.shift()
}
Queue.prototype.front = function(){
return this.queue[this.size() -1]
}
Queue.prototype.size = function(){
return this.queue.length
}
Queue.prototype.front = function(){
return this.size === 0
}
Queue.prototype.toString = function(){
return this.queue.join('')
}
2.4 队列的应用
3、链表
3.1链表的概述
链表 [Linked List]:链表是由一组不必相连【不必相连:可以连续也可以不连续】的内存结构 【节点】,按特定的顺序链接在一起的抽象数据类型。
3.2 链表的常规操作
方法 | 作用 |
---|---|
append(ele) | 向链表尾部添加一个元素 |
insert(index,ele) | 向链表的特定位置插入一个新的项 |
get(index) | 获取对应位置的元素 |
indexOf(ele) | 返回元素在链表的第一个匹配元素的索引,如果没有则返回-1 |
update(index) | 修改某个位置的元素 |
removeAt(index) | 从链表的特定位置移除当前元素 |
remove(ele) | 从链表种移除该元素 |
isEmpty() | 如果链表没有任何元素,返回true,否则返回false |
size() | 返回链表包含的元素个数。与数组的length属性类似 |
toString() | 由于链表使用了Node类,需要重写toString()方法,让其只输出元素的值 |
3.3 用js封装链表
function LinkedList (){
//链表头
this.head = null
this.length = 0
}
// 创建链表节点
function Node(data){
this.data = data
this.next = null
}
LinkedList.prototype.append = function(data){
let newNode = new Node(data)
if(this.length === 0){
this.head = newNode
}else{
let currentNode = this.head
while(currentNode.next){
currentNode = currentNode.next
}
currentNode.next = newNode
}
this.length += 1
}
LinkedList.prototype.insert = function(index, data){
let newNode = new Node(data)
if(index < 0 || index > this.length) return false
//插入的节点为第一个
if(index === 0){
newNode.next = this.head
this.head = newNode
}else{
let currentNode = this.head,
curIndex = 0,
previous = null
while(curIndex ++ < index){
previous = currentNode
currentNode = currentNode.next
}
newNode.next = currentNode
previous.next = newNode
}
this.length ++
return true
}
LinkedList.prototype.get = function(index){
if(index < 0 || index > this.length) return null
let curNode = this.head,
curIndex = 0
while(curIndex++ < index){
curNode = curNode.next
}
return curNode.data
}
LinkedList.prototype.indexOf = function(item){
let curNode = this.head,
curIndex = 0
while(curNode){
curNode = curNode.next
if(curNode && curNode.data == item){
return curIndex
}
}
return -1
}
LinkedList.prototype.update = function(index, item){
if(index < 0 || index > this.length) return false
let curNode = this.head,
curIndex = 0
while(curIndex++ < index){
curNode = curNode.next
}
curNode.data = item
}
LinkedList.prototype.removeAt = function(index){
if(index < 0 || index > this.length) return null
if(index === 0){
this.head = null
}else{
let curNode = this.head,
previous = null,
curIndex = 0
while(curIndex++ < index){
previous = curNode
curNode = curNode.next
}
previous.next = curNode.next
}
this.length --
}
LinkedList.prototype.remove = function(data){
let index = this.indexOf(data)
this.removeAt(index)
}
LinkedList.prototype.isEmpty = function(){
return this.length > 0 ? true : false
}
LinkedList.prototype.toString = function() {
let res = '',
currentNode = this.head
while(currentNode){
res += currentNode.data
currentNode = currentNode.next
}
return res
}
3.4 链表的应用
4、集合
4.1 集合的概述
集合通常是由一组无序、不能重复的元素构成。不能通过下标进行访问
4.2 集合的常规操作
方法 | 作用 |
---|---|
add(value) | 向集合添加一个元素 |
remove(value) | 从集合移除一个元素 |
has(value) | 如果在集合中,返回true,否则返回false |
clear() | 移除集合中的所有项 |
size() | 返回集合所包含元素的数量,与数组的length属性类似 |
values() | 返回一个包含集合所有值的数组 |
4.3 用js封装集合
function Set(){
this.items = {}
}
Set.prototype.add = function(value){
if(this.has(value)) return false
this.items[value] = value
return true
}
Set.prototype.has = function(value){
return this.items.hasOwnProperty(value)
}
Set.prototype.remove = function(value){
if(!this.has(value)) return false
delete this.items[value]
return true
}
Set.prototype.clear = function(){
this.items = {}
}
Set.prototype.size = function(){
return Object.keys(this.items).length
}
Set.prototype.values = function(){
return Object.keys(this.items)
}
4.4 集合的应用
5、哈希表
5.1 哈希表的概述
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
5.2 哈希表的常规操作
方法 | 作用 |
---|---|
hashFunc(str,size) | 将字符串或者数字哈希化 |
put(key,value) | 向哈希表添加或者更新一个元素 |
remove(key) | 从哈希表中移除一个元素 |
get(key) | 查询哈希表中的某个值 |
isEmpty() | 判断哈希表是否为空 |
5.3 用js封装哈希表
function HashTable(limit){
this.storage = []
this.count = 0
this.limit = limit
}
// 哈希函数
HashTable.prototype.hashFunc = function(str, size){
let hashCode = 0
//霍纳算法
for(let i=0;i< str.length;i++){
hashCode = hashCode * 37 + str.charCodeAt(i)
}
// 取余 && 位运算
let index = hashCode % size
return index;
}
HashTable.prototype.put =function(key, value){
let index = this.hashFunc(key, this.limit)
if(!this.storage[index]){
this.storage[index] = []
}
// 判断是否修改数据
for (let i = 0;i < this.storage[index].length; i++){
let tuple = this.storage[index][i]
if(tuple[0] == key){
tuple[1] = value
return
}
}
// 添加操作
this.storage[index].push([key,value])
this.count ++
}
HashTable.prototype.get = function(key){
// 1、根据key获取对应的index
let index = this.hashFunc(key, this.limit)
// 2、根据index获取对应的bucket
let bucket = this.storage[index]
// 3、线性查找值
if(!bucket){
return null
}
for(let i=0;i<bucket.length;i++){
let tuple = bucket[i]
if(tuple[0] === key){
return tuple[1]
}
}
// 4、如果都没找到,返回null
return null
}
HashTable.prototype.remove = function(key){
let index = this.hashFunc(key, this.limit),
bucket = this.storage[index]
if(!bucket){
return null
}
for (let i = 0; i< bucket.length; i++) {
let tuple = bucket[i];
if(tuple[0] == key){
bucket.splice(i,1)
this.count --
return tuple[1]
}
}
return null
}
HashTable.prototype.isEmpty = function(){
return this.count == 0
}
5.4 哈希表的应用
6、二叉搜索树
6.1 二叉树的概述
二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个结点最多只能有两棵子树,且有左右之分 。
6.2 二叉树的常规操作
方法 | 作用 |
---|---|
insert(key) | 向树插入一个新的值 |
search(key) | 在树中查找一个键,如果节点存在,则返回true;如果不存在,则返回false |
inOrderTraverse() | 通过中序遍历方式遍历所有节点 |
preOrderTraverse() | 通过先序遍历方式遍历所有节点 |
postOrderTraverse() | 通过后序遍历方式遍历所有节点 |
min() | 返回树中的最小的值 |
max() | 返回树中最大的值 |
remove(key) | 从树中移除某个值 |
6.3 用js封装二叉树
function BinarySearchTree(){
this.root = null
}
function CreateNode(key){
this.left = null
this.right = null
this.key = key
}
BinarySearchTree.prototype.insert = function(key){
// 1.创建节点
let node = new CreateNode(key)
// 2.判断根节点是否有值
if(this.root === null){
this.root = node
}else{
insertNode(this.root,node)
}
function insertNode(root, node){
if(node.key < root.key){
if(!root.left){
root.left = node
}else{
insertNode(root.left, node)
}
}else {
if(!root.right){
root.right = node
}else {
insertNode(root.right, node)
}
}
}
}
BinarySearchTree.prototype.inOrderTraverse = function(node){
if(!node) return
console.log(node.key)
this.inOrderTraverse(node.left)
this.inOrderTraverse(node.right)
}
BinarySearchTree.prototype.midOrderTraverse = function(node){
if(!node) return
this.midOrderTraverse(node.left)
console.log(node.key)
this.midOrderTraverse(node.right)
}
BinarySearchTree.prototype.postOrderTraverse = function(node){
if(!node) return
this.postOrderTraverse(node.left)
this.postOrderTraverse(node.right)
console.log(node.key)
}
BinarySearchTree.prototype.max = function(){
let node = this.root
while(node !== null && node.right){
node = node.right
}
return node.key
}
BinarySearchTree.prototype.min = function(){
let node = this.root
while(node !== null && node.left !== null){
node = node.left
}
return node.key
}
BinarySearchTree.prototype.search = function(key){
let node = this.root
while(node !== null){
if(key < node.key){
node = node.left
}else if(key > node.key){
node = node.right
}else{
return true
}
}
return false
}
BinarySearchTree.prototype.remove = function(key){
let current = this.root,
parent = null,
isLeftChild = true;
// 寻找该节点
while(current.key !== key){
parent = current
if(key < current.key){
isLeftChild = true
current = current.left
}else{
isLeftChild = false
current = current.right
}
// 遍历完没找到
if(current === null) return false
}
// 删除节点是叶子节点(没有子节点)
if(current.left === null && current.right === null){
if(current === this.root){
this.roo = null
}else if(isLeftChild){
parent.left = null
}else{
parent.right = null
}
}
// 删除节点有一个子节点
else if(current.right === null){
if(this.root === current){
this.root = current.left
}else if(isLeftChild){
parent.left = current.left
}else{
parent.right = current.left
}
}else if(current.left === null){
if(current == this.root){
this.root = current.left
}else if(isLeftChild){
parent.left = current.right
}else{
parent.right = current.right
}
}
// 删除节点有两个子节点
else{
let successor = getSuccessor(current);
if(this.root === current){
this.root = successor
}else if(isLeftChild){
parent.left = successor
}else{
parent.right = successor
}
successor.left = current.left
function getSuccessor(delNode){
let successerParent = delNode,
successer = delNode,
current = delNode.right;
while(current !== null){
successerParent = successer
successer = current
current =current.left
}
if(successer != delNode.right){
successerParent.left = successer.right
successer.right = delNode.right
}
}
return true
}
return false
}
6.4 二叉树的应用
把二叉树转换成累加树、将二叉搜索树变平衡、二叉搜索树最大键值和
7、红黑树
7.1 红黑树的概述
红黑树也是一个自平衡的二叉搜索树。在红黑树中,每个节点都遵循以下规则:
- 每个节点不是红的就是黑的
- 树的根节点都是黑的
- 所有的叶子节点都是黑的
- 如果一个节点是红的,那么它的两个子节点都是黑的
- 不能有相邻的两个红节点
- 从给定的节点到它的后代节点的所有路径包含相同数量的黑色节点
8、图
8.1 图的概述
图是网路结构的抽象模型。主要研究的目的是事物之间的关系,顶点代表事物,边代表两个事物间的关系。
- 顶点:图中的一个节点,如图中的某个村庄
- 边:顶点和顶点之间的连线,如图中0-1有一条边,1-2有一条边,0-2没有边
- 相邻顶点:一条边连接在一起的顶点称为相邻顶点,如图0-1是相邻的,0-2不相邻
- 度:一个顶点的度是相邻顶点的数量,如图0顶点的数量是2
- 路径:路径是顶点v1,v2,v3…vn的一个连续序列
8.2 图的常规操作
方法 | 作用 |
---|---|
addVertex(v) | 添加顶点 |
addEdge(v1,v2) | 添加边 |
toString() | |
bfs() | 广度遍历图 |
dfs() | 深度遍历图 |
8.3 用js封装图
function Graph() {
//属性:顶点、边
this.vertexes = []
this.edges = new Dictionay()
}
// 添加顶点
Graph.prototype.addVertex = function(v){
this.vertexes.push(v)
this.edges.set(v, [])
}
// 添加边
Graph.prototype.addEdge = function (v1,v2){
this.edges.get(v1).push(v2)
this.edges.get(v2).push(v1)
}
Graph.prototype.toString = function (){
let res = ''
for(let i=0;i< this.vertexes.length; i++){
res += this.vertexes[i] + '--->'
let vEdges = this.edges.get(this.vertexes[i])
for(let j=0;j < vEdges;j++){
res += vEdges
}
}
return res
}