第一节:解密QQ号-队列
书中描述的过程很详细啊,我直接照着过程自己先写了个
let arr = [6, 3, 1, 7, 5, 8, 9, 2, 4];
let res = [];
while (arr.length > 0) {
res.push(arr.shift());
let head = arr.shift();
if (head) {
arr.push(head);
}
}
// 第一次写了个错误版本:死循环
// arr 会一直有一个 undefined 元素
// while (arr.length >= 0) {
// res.push(arr.shift());
// arr.push(arr.shift());
// }
console.log(res);
大家可以根据我写的错误,来取取经叭,我个人也要多反思,像这样的错误就是因为程序的边界细节的处理不好,说的简单点就是想得不够周到,多吃核桃吧。
插一嘴:因为写了上面的死循环bug,然后调试到浏览器中,发现打开不了控制台啊,于是写了这篇文章: vscode 配置调试工具 JavaScript 调试
因为js给的方法多,所以写的简洁,我们来实现一下书中的代码:
let q = new Array(102).fill(0);
let init = [0, 6, 3, 1, 7, 5, 8, 9, 2, 4];
for (let i = 0; i < init.length; i++) {
q[i] = init[i];
}
let head = 1,
tail = 10;
while (head < tail) { // 此条件说明队列不为空
console.log(q[head]);
head++; // 模拟队首出队
q[tail] = q[head];
tail++;
head++;
}
模拟结构体实现
class Queue{
constructor(){
this.data = new Array(100).fill(0);
this.head = 1;
this.tail = 10;
}
}
let q = new Queue();
let init = [0, 6, 3, 1, 7, 5, 8, 9, 2, 4];
for (let i = 0; i < init.length; i++) {
q.data[i] = init[i];
}
while (q.head < q.tail) { // 此条件说明队列不为空
console.log(q.data[q.head]);
q.head++; // 模拟队首出队
q.data[q.tail] = q.data[q.head];
q.tail++;
q.head++;
}
第二节:解密回文-栈
因为栈的特点,只需要一个指向栈顶的指针了
书中得栗子实例如下:
let s = []; // 栈容器
// let str = "ahaha";
let str = "ahahaa";
let len = str.length;
let mid = (len >> 1) - 1; // 注意这里要减1
let stackTop = 0;
for (let i = 0; i <= mid; i++) {
s[++stackTop] = str[i];
}
console.log(s); // [empty,a,h,a]
let next;
if (len % 2 == 0) {
next = mid + 1;
} else {
next = mid + 2;
}
for (let j = next; j < len; j++) {
if (str[j] != s[stackTop]) {
break;
}
stackTop--;
}
if (stackTop === 0) {
console.log("是回文字符串");
} else {
console.log("不是回文字符串");
}
第三节:纸牌游戏
这个代码有一丢丢长,是栈和队列的应用
let xiaoheng = [2, 4, 1, 2, 5, 6];
let xiaoha = [3, 1, 3, 5, 6, 4];
let book = new Array(10).fill(0);
class Queue {
constructor() {
// 这里要写成this,刚开始我写成let了...
this.data = [];
this.head;
this.tail;
}
}
class Stack {
constructor() {
this.data = [];
this.top;
}
}
let q1 = new Queue();
let q2 = new Queue();
let s = new Stack();
q1.head = 1;
q1.tail = 1;
q2.head = 1;
q2.tail = 1;
s.top = 0;
for (let i = 1; i <= xiaoheng.length; i++) {
q1.data[i] = xiaoheng[i - 1];
q1.tail++;
}
for (let j = 1; j <= xiaoha.length; j++) {
q2.data[q2.tail] = xiaoha[j - 1];
q2.tail++;
}
while (q1.head < q1.tail && q2.head < q2.tail) {
// 小哼出牌
t = q1.data[q1.head];
if (book[t] === 0) {
// 没有赢牌
q1.head++;
s.top++;
s.data[s.top] = t;
book[t] = 1;
}
else {
q1.head++;
q1.data[q1.tail] = t;
q1.tail++;
while (s.data[s.top] != t) {
book[s.data[s.top]] = 0;
q1.data[q1.tail] = s.data[s.top];
s.top--;
q1.tail++;
}
}
// 小哈出牌
t = q2.data[q2.head];
if (book[t] === 0) {
// 没有赢牌
q2.head++;
s.top++;
s.data[s.top] = t;
book[t] = 1;
}
else {
q2.head++;
q2.data[q2.tail] = t;
q2.tail++;
while (s.data[s.top] != t) {
book[s.data[s.top]] = 0;
q2.data[q2.tail] = s.data[s.top];
s.top--;
q2.tail++;
}
}
}
if (q2.head = q2.tail) {
console.log('小哼win');
console.log('小哼的手牌是:',q1.data.slice(q1.head, q1.tail));
// for (let i = q1.head; i <= q1.tail - 1; i++) {
// console.log(q1.data[i]);
// }
if (s.top > 0) {
console.log('桌上的牌是:',s.data.slice(1,s.top + 1));
// for (let i = 1; i <= s.top; i++) {
// console.log(s.data[i]);
// }
}
} else {
console.log('小哈win');
console.log('小哈的手牌是:',q2.data.slice(q2.head, q2.tail));
// for (let i = q2.head; i <= q2.tail - 1; i++) {
// console.log(q2.data[i]);
// }
if (s.top > 0) {
console.log('桌上的牌是:');
for (let i = 1; i <= s.top; i++) {
console.log(s.data[i]);
}
}
}
第四节:链表
这部分主要的是如何建立链表,具体的思路就是,建立一个节点,然后把前一个节点的next 指向新节点,然后把前一个结点更新。
在看这部分的时候,我就有个疑问:如何把链表连在一起的啊,最关键的一步是q = p;
,其中q
就记录着前一个节点(我当时没读懂这个才迷惑的)。
// 创建链表
function ListNode(val) {
this.val = val;
this.next = null;
}
// 建立链表
let arr = [2, 3, 5, 8, 9, 10, 18, 26, 32];
let head = null;
let q = new ListNode();
for (let i = 0, len = arr.length; i < len; i++) {
let p = new ListNode(arr[i]);
p.next = null;
if (head === null) {
head = p;
} else {
q.next = p; // 上一个节点的后继指针指向当前创建的节点。
}
q = p; // q是上一个节点
}
console.log(head);
let t = head;
while(t){
console.log(t.val);
t = t.next;
}
接下俩讲了如何增添一个节点到链表中,这里插入一个6
。
function ListNode(val) {
this.val = val;
this.next = null;
}
// 建立链表
let arr = [2, 3, 5, 8, 9, 10, 18, 26, 32];
let head = null;
let q = new ListNode();
for (let i = 0, len = arr.length; i < len; i++) {
let p = new ListNode(arr[i]);
if (head === null) {
head = p;
} else {
q.next = p;
}
q = p;
}
// console.log(head);
let t = head;
while (t) {
if (t.next.val > 6) {
let p = new ListNode(6);
p.next = t.next;
t.next = p;
break;
}
t = t.next;
}
while (head) {
console.log(head.val);
head = head.next;
}
第五节:模拟链表
模拟链表的思想其实都差不多,只是把指针的部分换成数组了。
我们定义一个数组 right
,用来保存位置,什么位置呢?当前序列中每一个元素右边的元素在数组 data 中的位置`。很绕对吧?这是书里的原话,emm尝试多读几遍吧!!
let data = [0, 2, 3, 5, 8, 9, 10, 18, 26, 32];
let right = [];
// 初始化right数组
let len = data.length - 1;
for (let i = 1; i < len; i++) {
right[i] = i + 1;
}
right[len] = 0;
len++;
data[len] = 6;// 插入的数字
let t = 1; // t 相当于 index 吧
while (t != 0) {
// 如果右侧节点大于要插入的节点
if (data[right[t]] > data[len]) {
right[len] = right[t];
right[t] = len;
break;
}
t = right[t];
}
console.log(data, right);
// 读取数字
let t1 = 1;
while (t1) {
console.log(data[t1]);
t1 = right[t1];
}