数据结构
栈
-
受限的线性结构
-
后进先出 LIFO
-
栈定、栈底
基于数组实现
属性
item: []
常见方法
function Stack() {
// 属性
this.item = [];
// push
Stack.prototype.push = function (element) {
this.item.push(element);
};
// pop
Stack.prototype.pop = function () {
return this.item.pop();
};
// peek
Stack.prototype.peek = function () {
return this.item[this.item.length - 1];
};
// isEmpty
Stack.prototype.isEmpty = function () {
return this.item.length === 0;
};
// size
Stack.prototype.size = function () {
return this.item.length;
};
// toString
Stack.prototype.toString = function () {
let res = "";
for (let i = 0; i < this.item.length; i++) {
res += this.item[i] + " ";
}
return res;
};
}
应用
十进制转二进制
function dec2bin(decNumber) {
// 栈对象-保存余数
const stack = new Stack();
while (decNumber > 0) {
stack.push(decNumber % 2);
// 获取商,作为下一次的decNumber
decNumber = Math.floor(decNumber / 2);
}
// 从栈中取出0和1
let binRes = "";
while (!stack.isEmpty()) {
binRes += stack.pop();
}
return binRes;
}
链表
数组的缺点
- 连续的内存空间,大部分编程语言的数组大小固定,当不能满足容量需求时,需要扩容,而扩容是非常消耗性能
- 在头部或中间插入数据,需要进行大量元素的位移
链表的出现
优点
相对于数组,链表有一些优点:
- 内存空间不必连续~,灵活
- 不用创建时确定大小
- 插入和删除,时间复杂度可以达到 O(1)
缺点
而对于数组,链表也有一些缺点:
- 访问任何一个元素,都需要从头开始
总结
侧重查询 | 选数组 |
---|---|
侧重插入和删除 | 选链表 |
链表和火车的结构非常相似~
实现
单向链表
属性
- head
- length
- node 的 data
- node 的 next
function LinkedList() {
// 属性
this.head = null;
this.length = 0;
// 内部类 节点
function Node(data) {
this.data = data;
this.next = null;
}
}
常见操作
- append
- insert
- get
- update
- remove
- removeAt
- indexOf
- size
- empty
- toString
append
- 创建新节点
- 添加的是第一个节点?
- 找出最后节点
- 长度+1
LinkedList.prototype.append = function (data) {
// 创建新节点
const newNode = new Node(data);
// 添加的是第一个节点?
if (this.length === 0) {
this.head = newNode;
} else {
// 不是
// 找出最后节点
let last = this.head;
while (last.next) {
last = last.next;
}
// 最后节点指向新加节点
last.next = newNode;
}
// 长度+1
this.length += 1;
};
toString
LinkedList.prototype.toString = function (params) {
let current = this.head;
let res = "";
// 遍历节点
while (current) {
res += current.data + " ";
current = current.next;
}
return res;
};
测试
const list = new LinkedList()
list.append('abc')
list.append('abb')
list.append('ccc')
console.log(list)
insert
- position 越界判断
- 根据 data 创建新节点
- case
- 长度+1
LinkedList.prototype.insert = function (position, data) {
// position越界判断
if (position < 0 || position > this.length) {
return false;
}
// 根据data创建新节点
const newNode = new Node(data);
// case1 插入position = 0 的位置
if (position === 0) {
newNode.next = this.head;
this.head = newNode;
} else {
// case2 插入position > 1 的位置
// 找到位置
let index = 0;
// 记录目标节点
let current = this.head;
// 记录目标节点的上一个节点
let previous = null;
while (index++ < position) {
previous = current;
current = current.next;
}
newNode.next = current;
previous.next = newNode;
}
// 长度+1
this.length += 1;
return true;
};
其中,插入到最后的情况,case2 已经包含,而且,要是想插入到最后,用append呗
测试
// 测试
const list = new LinkedList();
// append
list.append("abc");
list.append("abb");
list.append("ccc");
// insert
list.insert(0, "zsf");
list.insert(3, "hhh");
list.insert(100, "lll");
console.log(list);
get
- position 越界判断,=也不行
- 获取 data
LinkedList.prototype.get = function (position) {
// position越界判断,=也不行
if (position < 0 || position >= this.length) {
return null;
}
// 获取data
let current = this.head;
let index = 0;
while (index++ < position) {
current = current.next;
}
return current.data;
};
测试
const list = new LinkedList();
// append
list.append("abc");
list.append("abb");
list.append("ccc");
// insert
list.insert(0, "zsf");
list.insert(3, "hhh");
list.insert(100, "lll");
// get
console.log(list.get(0));
console.log(list.get(2));
console.log(list.get(9));
console.log(list);
indexOf
LinkedList.prototype.indexOf = function (data) {
let current = this.head;
let index = 0;
while (current) {
if (current.data === data) {
return index;
}
current = current.next;
index += 1;
}
return -1;
};
测试
const list = new LinkedList();
console.log(list.indexOf("zsf"));
console.log(list.indexOf("hhh"));
console.log(list.indexOf("ll"));
update
- position 越界判断,=也不行
- 找到目标节点
LinkedList.prototype.update = function (position, data) {
// position越界判断,=也不行
if (position < 0 || position >= this.length) {
return false;
}
// 找到目标节点
let current = this.head;
let index = 0;
while (index++ < position) {
current = current.next;
}
current.data = data;
return true;
};
测试
const list = new LinkedList();
console.log(list.update(0, "hhh"));
removeAt
position 越界判断,=也不行
- 第一个节点?
- 目标节点的上一个节点
- 长度-1
LinkedList.prototype.removeAt = function (position) {
// position越界判断,=也不行
if (position < 0 || position >= this.length) {
return null;
}
// 第一个节点?
// 目标节点
let current = this.head;
if (position === 0) {
this.head = this.head.next;
} else {
// 目标节点的上一个节点
let previous = null;
let index = 0;
while (index++ < position) {
previous = current;
current = current.next;
}
previous.next = current.next;
}
// 长度-1
this.length -= 1;
return current.data;
};
测试
const list = new LinkedList();
console.log(list.removeAt(0));
remove
- 根据 data 获取位置
- 根据位置删除
LinkedList.prototype.remove = function (data) {
// 根据data获取位置
const position = this.indexOf(data);
// 根据位置删除
return this.removeAt(position);
};
测试
const list = new LinkedList();
console.log(list.remove("abc"));
isEmpty
LinkedList.prototype.isEmpty = function () {
return this.length === 0;
};
size
LinkedList.prototype.size = function () {
return this.length;
};
反转单链表
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var reverseList = function (head) {
let temp = null;
let pre = null;
let cur = head;
while (cur) {
cur = head.next;
head.next = pre;
prev = head;
head = p;
}
return pre;
};