ZONO's World

welcome to my world

每天

  • 在前一晚或者早上开始学习前,定一个笼统的计划(要花费至少2两小时以上的工作),避免无计划乱行动

阅读一篇文档时,按照:

  • 略读
  • 精读
    先划分好,以确认精力分配

观看一个视频时,应该:

  • 看完一个演示效果
  • 做一次笔记+练手

学习须知:

  • 以上任何方法都不要二极管,这只是一种偏向。
  • 不要妄想一次就记住,知识都需要重复,所以初学时,以通读理解为主。下次遇见了在细读记忆。
  • 切记不要看一步做一步,会干扰连续性。

对现阶段自己的话

  1. 快就是慢,慢就是快

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
const p1 = new promise((resovle,reject)=>{
resolve(value);//成功
reject(error);//失败
})//定义

p1.then(
(value)=>{
console.log("成功:"${value})
},
(err)=>{
console.log("失败:"${err})
}
);

开始手写前我们先得对promise对象,从架构上理解(图来源于网络)
promise架构图

如此便可以把基础的架构先准备好。

1
2
3
4
5
6
7
8
9
10
class Promise {

// 构造函数
// resolve
// reject
// then
// catch
// finally
// ...
}

接下来让我们一个一个实现它们。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//起步构建
// 1.用类创建Promise,类中需要有个执行器executor
// 2.执行者中发生错误,交给异常状态处理
// 3.执行者中状态只能触发一次,状态触发一次之后,不能修改状态
// 4.执行者中的this,由调用执行者的作用域决定,因此我们需要将执行者中的this绑定为我们创建的Promise对象。
// 5.在构造函数中需要为Promise对象创建status和value记录Promise的状态和传值。
// 6.在构造函数中需要为Promise对象创建callbacks数组,用于存储then方法中的回调函数。
class MyPromise {
static PENDING = "pending";
static FULFILLED = "fulfilled";
static REJECTED = "rejected";

constructor(executor) {
this.status = MyPromise.PENDING; // 初始化状态为pending
this.value = null; // 初始化值为null
this.callbacks = []; // 初始化回调数组为空
try {
executor(this.resolve.bind(this), this.reject.bind(this)); // 执行executor函数,并传入resolve和reject函数作为参数
} catch (error) {
this.reject(error); // 如果executor函数发生错误,则将状态设置为rejected
}
}

resolve(value) {
if (this.status == MyPromise.PENDING) { // 只有在状态为pending时才能改变状态
this.status = MyPromise.FULFILLED; // 将状态设置为fulfilled
this.value = value; // 保存传入的值
setTimeout(() => {
this.callbacks.map((item) => {
item.onFulfilled(this.value); // 执行所有成功回调函数
});
});
}
}

reject(reason) {
if (this.status == MyPromise.PENDING) { // 只有在状态为pending时才能改变状态
this.status = MyPromise.REJECTED; // 将状态设置为rejected
this.value = reason; // 保存传入的原因
setTimeout(() => {
this.callbacks.map((item) => {
item.onRejected(this.value); // 执行所有失败回调函数
});
});
}
}

//开始写then方法
//1.then接收2个参数,一个成功回调函数,一个失败回调函数
//2.then中发生错误,状态为rejected,交给下一个then处理
//3.then返回的也是一个Promise
//4.then的参数值可以为空,可以进行传值穿透
//5.then中的方法是异步执行的
//6.then需要等promise的状态改变后,才执行,并且异步执行
//7.then是可以链式操作的
//8.then的onFulfilled可以用来返回Promise对象,并且then的状态将以这个Promise为准
//9.then的默认状态是成功的,上一个Promise对象的状态不会影响下一个then的状态
//10.then返回的promise对象不是then相同的promise
then(onFulfilled, onRejected) {
if (typeof onFulfilled != "function") { // 如果成功回调不是函数,则将其设置为默认的返回值函数
onFulfilled = (value) => value;
}

if (typeof onRejected != "function") { // 如果失败回调不是函数,则将其设置为默认的返回原因函数
onRejected = (reason) => reason;
}

let promise = new MyPromise((resolve, reject) => {
if (this.status == MyPromise.FULFILLED) { // 如果当前Promise状态为fulfilled,则异步执行成功回调
setTimeout(() => {
this.parse(promise, onFulfilled(this.value), resolve, reject);
});
}

if (this.status == MyPromise.REJECTED) { // 如果当前Promise状态为rejected,则异步执行失败回调
setTimeout(() => {
this.parse(promise, onRejected(this.value), resolve, reject);
});
}

if (this.status == MyPromise.PENDING) { // 如果当前Promise状态为pending,则将回调函数存储起来
this.callbacks.push({
onFulfilled: (value) => {
this.parse(promise, onFulfilled(value), resolve, reject);
},
onRejected: (reason) => {
this.parse(promise, onRejected(reason), resolve, reject);
},
});
}
});
return promise; // 返回新的Promise对象
}

//整理冗余代码
parse(promise, result, resolve, reject) {
if (promise == result) { // 如果返回的Promise与当前Promise相同,则抛出循环引用错误
throw new TypeError("Chaining cycle detected for promise");
}
try {
if (result instanceof MyPromise) { // 如果返回值是Promise对象,则继续调用then方法
result.then(resolve, reject);
} else {
resolve(result); // 否则,将返回值作为新Promise的成功值
}
} catch (error) {
reject(error); // 如果发生错误,则将错误作为新Promise的失败原因
}
}

//Promise的静态方法,resolve
static resolve(value) {
return new MyPromise((resolve, reject) => {
if (value instanceof MyPromise) { // 如果传入的值是Promise对象,则继续调用then方法
value.then(resolve, reject);
} else {
resolve(value); // 否则,将传入的值作为新Promise的成功值
}
});
}

//Promise的静态方法,reject
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason); // 将传入的原因作为新Promise的失败原因
});
}

//Promise的静态方法,all
static all(promises) {
let values = [];
return new MyPromise((resolve, reject) => {
promises.forEach((promise) => {
if (promise.status == MyPromise.FULFILLED) { // 如果有一个Promise状态为fulfilled,则将其值存入数组
values.push(promise.value);
} else if (promise.status == MyPromise.REJECTED) { // 如果有一个Promise状态为rejected,则直接将整个Promise设置为rejected
reject(promise.value);
}
if (values.length == promises.length) { // 如果所有Promise都已经fulfilled,则将数组作为新Promise的成功值
resolve(values);
}
});
});
}

//Promise的静态方法,race
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach((promise) => {
promise.then((value) => {
resolve(value); // 只要有一个Promise状态改变,则将其值作为新Promise的成功值
});
});
});
}

//Promise的静态方法,race
catch(onRejected) {
return this.then(null, onRejected); // catch方法实际上是then方法的简写,只传入失败回调
}
}

照抄的,自己如果真的遇见手写可能会有点蒙b!

该文主要是帮助直接梳理知识点,具体讲解可以点击文中连接,大佬的讲的内容好得多

一、CSS发展历史

现代 CSS 进化史 - 知乎 (zhihu.com)(只浏览了)

  • 浮动的布局:2006年,A List Apart 上发表了一篇热门文章 In Search of the Holy Grail,文章概述了实现圣杯布局的详细方法——一个头部、三列内容和一个底部,你一定觉得一个简单的布局被称为圣杯布局很疯狂吧,但是在当时纯 CSS 的时代这的确很难实现。
  • 基于 Flexbox 的布局:Flexbox CSS 属性实在2009年第一次提出来的,但直到2015年才得到浏览器的广泛支持。
  • 基于 Grid的布局:CSS网格最早在2011年提出的(比flexbox提案晚不了多久),但是花了好长时间才在浏览器上普及起来。截止2018年初,大多数现代浏览器都已经支持CSS grid(这比一两年前有巨大的进步了)
  • 使用 CSS 预处理器扩展 CSS 语法

二、单位

一文读懂 CSS 单位 - 知乎 (zhihu.com)

知乎上的一个图

px:像素

相对单位

字体

em:1em = font-size的值(大概,如果嵌套起来,就按链计算)

rem:按根来算

ex:所用字体中小写字母 x 的高度。

ch:基于数字 0 的宽度计算的。会随着字体的变化而变化。

视窗(CSS3)

  • vw:视窗宽度的百分比;(css3)
  • vh:视窗高度的百分比;(css3)

min-height: 50vh;在全局中加上这个,那么显示就只有一半页面

  • vmax:较大的 vh 和 vw;(css3)
  • vmin:较小的 vh 和 vw。(css3)

vmin 和 vmax 与视窗宽度和高度的最大值和最小值有关。假如一个浏览器高度为500px,宽度为1200px,那么1vmin就是5px,1vmax就是12px。

绝对单位

1
2
3
4
5
1in = 25.4mm = 2.54cm = 6pc(Picas,印刷术语) = 72pt(Point,印刷术语) =96px
1pc = 16px
1cm = 37.8px
1mm = 3.78px
1in(inches,英尺) = 96px

频率、时间、分辨率单位

通常情况下,频率单位使用在听或说级联样式表中。频率可以被用来改变一个语音阅读文本的音调。低频率就是低音,高频率就是高音。

1
1kHz = 1000Hz

时间单位主要用于过度和动画中,用于定义持续时间或延迟时间。下面两种定义是等效的:

1
2
3
4
5
6
7
8
/*1s = 1000ms*/
a[href] {
transition-duration: 2.5s;
}

a[href] {
transition-duration: 2500ms;
}

主要用于媒体查询

1
2
3
1dppx = 96dpi
1dpi ≈ 0.39dpcm
1dpcm ≈ 2.54dpi

角度单位(CSS3)

一般这些角度单位用于元素的旋转操作,包括2D旋转、3D旋转等。

1
2
3
90deg(Degress,度) = 100grad(Radians,梯度) = 0.25turn(Turns,圈) ≈ 1.570796326794897rad(Radians,弧度)

360度=400梯度

百分比单位

邪门知识点(bushi)

font-size: 62.5%的意义

font-size: 62.5%的意义_font-size 62.5-CSDN博客

让1rem=16px变为1rem=10px

三、五种布局

CSS布局(重点整理) - 知乎 (zhihu.com)

流式布局:块和内联

绝对定位(position)

CSS position 属性用于指定一个元素在文档中的定位方式。

浮动(float)

float CSS 属性指定一个元素应沿其容器的左侧或右侧放置,允许文本和内联元素环绕它。该元素从网页的正常流动(文档流)中移除,但是仍然保持部分的流动性(与绝对定位相反)。

表格(table)

弹性(flex)

Flex 布局教程:语法篇 - 阮一峰的网络日志 (ruanyifeng.com)

是什么?

Flex 布局示例 (vgee.cn)

这是一位网友根据阮一峰大佬的文章写的实现,可以按F12去样式里面调着玩玩

容器的属性

1
2
3
4
5
6
flex-direction: row | row-reverse | column | column-reverse;
flex-wrap: nowrap | wrap | wrap-reverse;
flex-flow: <flex-direction> || <flex-wrap>;/*简写*/
justify-content: flex-start | flex-end | center | space-between | space-around;
align-items: flex-start | flex-end | center | baseline | stretch;
align-content: flex-start | flex-end | center | space-between | space-around | stretch;

项目的属性

1
2
3
4
5
6
order: <integer>;
flex-grow: <number>; /* default 0 */
flex-shrink: <number>; /* default 1 */
flex-basis: <length> | auto; /* default auto */
flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]/*是flex-grow, flex-shrink 和 flex-basis的简写*/
align-self: auto | flex-start | flex-end | center | baseline | stretch;

实战

Flex 布局教程:实例篇 - 阮一峰的网络日志 (ruanyifeng.com)

网格(grid)

CSS Grid 网格布局教程 - 阮一峰的网络日志 (ruanyifeng.com)

容器属性

1
2
3
display: grid;
grid-template-columns:...;
grid-template-rows:repeat();

1. grid-template-columns 属性, grid-template-rows 属性

(1)repeat()

(2)auto-fill 关键字:自动填充

(3)fr 关键字:比例

1
2
3
4
.container {
display: grid;
grid-template-columns: 150px 1fr 2fr;
}

**(4)minmax()**:产生一个长度范围,表示长度就在这个范围之中

1
grid-template-columns: 1fr 1fr minmax(100px, 1fr);

(5)auto 关键字

1
grid-template-columns: 100px auto 100px;

基本上等于该列单元格的最大宽度,除非单元格内容设置了min-width

(6)网格线的名称

使用方括号,指定每一根网格线的名字(允许同一根线有多个名字)

1
2
grid-template-columns: [c1] 100px [c2] 100px [c3] auto [c4];
grid-template-rows: [r1] 100px [r2] 100px [r3] auto [r4];

2.grid-row-gap 属性, grid-column-gap 属性, grid-gap 属性

grid-row-gap属性设置行与行的间隔(行间距)。

grid-column-gap属性设置列与列的间隔(列间距)。

grid-gap属性是grid-column-gapgrid-row-gap的合并简写形式。grid-gap: <grid-row-gap> <grid-column-gap>;

如果省略第二个值,自动认为第二个值等于第一个值

1
2
3
4
5
.container {
grid-row-gap: 20px;
grid-column-gap: 20px;
/* = grid-gap: 20px 20px;*/
}

根据最新标准,上面三个属性名的grid-前缀已经删除,grid-column-gapgrid-row-gap写成column-gaprow-gapgrid-gap写成gap

3.grid-template-areas 属性

用于定义区域。该定义与后面的项目属性中的grid-area 联动

1
2
3
4
5
6
7
8
.container {
display: grid;
grid-template-columns: 100px 100px 100px;
grid-template-rows: 100px 100px 100px;
grid-template-areas: 'a b c'
'd e f'
'g h i';
}

区域的命名会影响到网格线。

4.grid-auto-flow 属性

默认的放置顺序是”先行后列”,这个顺序由grid-auto-flow属性决定,默认值是row,即”先行后列”。也可以将它设成column,变成”先列后行”。

1
grid-auto-flow: column;

5.justify-items 属性, align-items 属性, place-items 属性

justify-items属性设置单元格内容的水平位置(左中右),align-items属性设置单元格内容的垂直位置(上中下),place-items属性是align-items属性和justify-items属性的合并简写形式。place-items: <align-items> <justify-items>;

1
2
3
4
5
.container {
justify-items: start;
align-items: start;
/*place-items: start end;*/
}

6.justify-content 属性, align-content 属性, place-content 属性

justify-content属性是整个内容区域在容器里面的水平位置(左中右),align-content属性是整个内容区域的垂直位置(上中下)。place-content属性是align-content属性和justify-content属性的合并简写形式。

1
2
3
4
.container {
justify-content: start | end | center | stretch | space-around | space-between | space-evenly;
align-content: start | end | center | stretch | space-around | space-between | space-evenly;
}

7.grid-auto-columns 属性, grid-auto-rows 属性

用来设置,浏览器自动创建的多余网格的列宽和行高。

8.grid-template 属性, grid 属性

grid-template属性是grid-template-columnsgrid-template-rowsgrid-template-areas这三个属性的合并简写形式。

grid属性是grid-template-rowsgrid-template-columnsgrid-template-areasgrid-auto-rowsgrid-auto-columnsgrid-auto-flow这六个属性的合并简写形式。

项目属性

1.grid-column-start 属性, grid-column-end 属性, grid-row-start 属性, grid-row-end 属性

  • grid-column-start属性:左边框所在的垂直网格线
  • grid-column-end属性:右边框所在的垂直网格线
  • grid-row-start属性:上边框所在的水平网格线
  • grid-row-end属性:下边框所在的水平网格线
1
2
3
4
5
.item-1 {
grid-column-start: 2;
grid-column-end: 4;
}
/*第二条线出发,第四条结束*/

grid-auto-flow属性决定排列,columnrow densecolumn dense也生效,这四个属性的值,除了指定为第几个网格线,还可以指定为网格线的名字。

span关键字,表示”跨越”,即左右边框(上下边框)之间跨越多少个网格。

1
2
3
.item-1 {
grid-column-start: span 2;
}

2. grid-column 属性, grid-row 属性

grid-column属性是grid-column-startgrid-column-end的合并简写形式,grid-row属性是grid-row-start属性和grid-row-end的合并简写形式。

1
2
3
4
.item {
grid-column: <start-line> / <end-line>;
grid-row: <start-line> / <end-line>;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.item-1 {
grid-column: 1 / 3;
grid-row: 1 / 2;
}
/* 等同于 */
.item-1 {
grid-column-start: 1;
grid-column-end: 3;
grid-row-start: 1;
grid-row-end: 2;
}
/* 等同于 */
.item-1 {
background: #b03532;
grid-column: 1 / span 2;
grid-row: 1 / span 2;
}

斜杠以及后面的部分可以省略,默认跨越一个网格。

3.grid-area 属性

grid-area属性指定项目放在哪一个区域。

grid-area属性还可用作grid-row-startgrid-column-startgrid-row-endgrid-column-end的合并简写形式,直接指定项目的位置。

1
2
3
.item {
grid-area: <row-start> / <column-start> / <row-end> / <column-end>;
}

4.justify-self 属性, align-self 属性, place-self 属性

justify-self属性设置单元格内容的水平位置(左中右),跟justify-items属性的用法完全一致,但只作用于单个项目。

align-self属性设置单元格内容的垂直位置(上中下),跟align-items属性的用法完全一致,也是只作用于单个项目。

place-self属性是align-self属性和justify-self属性的合并简写形式。

实例

CSS布局方案汇总(30个实例图文并茂) - 掘金 (juejin.cn)

最近看见太多three.js做出来的有意思的东西了于是准备系统的学一下

three.js

学习计划和学习记录

2023.11.25

计划

  • 搭建vue环境
  • 完成官网盒子

    过程

    安装+复制官网代码运行,整体来说十分简单。

踩坑

  1. 官方包是不支持Ts的(我使用的是vue+ts),所以要按照包@types/three
    npm i --save-dev @types/three
    另一个方法:
    创建或查找shims-vue.d.ts文件,加入:
    1
    2
    3
    4
    5
    6
    declare module '*.vue' {
    import Vue from 'vue'
    export default Vue
    }
    //declare module 'xxx'对应库名
    declare module 'three'
    重启后生效

2021.11.26

计划

待执行计划

  • 看连接文章

Three.js

概念

前置知识

webGl和OpenGl

前端也要懂图形化: 浅谈 WebGL 技术 - 掘金 (juejin.cn)(后序阅读)

WebGL是一项结合了HTML5和JavaScript,用来在网页上绘制和渲染复杂三维图形的技术。WebGL通过JavaScript操作OpenGL接口的标准,把三维空间图像显示在二维的屏幕上。

OpenGL是开放式图形标准,跨编程语言、跨平台,Javascript、Java 、C、C++ 、 python 等都能支持OpenGL ,OpenGL的Javascript实现就是WebGL。OpenGL ES 2.0是OpenGL的子集,针对手机、游戏主机等嵌入式设备而设计。

Canvas

Canvas是HTML5的画布元素,在使用Canvas时,需要用到Canvas的上下文,可以用2D上下文绘制二维的图像,也可以使用3D上下文绘制三维的图像,其中3D上下文就是指WebGL。

Threejs

Three.js是基于webGL的封装的一个易于使用且轻量级的3D库

应用场景

3D游戏、3D模型展示、数据可视化、web VR、

例子:跳一跳小游戏

前置准备

  • 浏览器
  • 基础的几何知识(三维坐标系)
  • 模块化导入库知识

开始

1. 三大要素

场景(scene)、相机(camera)、渲染器(renderer)

建模软件也是这一套逻辑

场景

var scene = new THREE.Scene()

相机

透视投影相机(perspectiveCamera)【重要】

var camera = new THREE.PerspectiveCamera(fov, aspect, near, far);

正交投影相机(OrthographicCamera )

微信跳一跳就是用的这个吧

var camera = new THREE.OrthographicCamera( left,right, top,bottom, near, far )

渲染器

var renderer = new THERR.WebGLRenderer();

2.基本元素

建模软件也是这一套逻辑+1

物体
  • 几何体模型(Geometry):物体
  • 材质(Material):皮肤
  • 网格(Mesh):物体的展现形式
光源

TCP和UDP

区别

一个基于连接,一个基于非连接

TCP(全双工)

建立连接:三次握手

客户端发往服务器how are you?(SYN)

服务器回应I’m find and you?(SYN+ACK)

客户端回应me too。(ACK)

关闭连接:四次挥手

都发一次fin+ack

UDP(非连接)

稳定性弱,隧道网络

算法通关村第一关——链表中环的问题和双相链表

在操作系统、JVM等基础框架中运用广泛

1. 链表中的环问题

  1. 如何确定链表中存在环
  2. 假如环存在,如何确定环的入口
  3. 理解双向链表的含义和操作方法

1.1. 如何确定链表中是否有环(LeeCode141)

  • 如何确定链表中是否有环(LeeCode141)
  • 有环的话环在哪里(LeeCode142)
方法一、运用hash,将要遍历的元素放入map中
1
2
3
4
5
6
7
8
9
10
class Solution:
def hasCycle(self,head):
#python
seen = set()
while head:
if head in seen:
return True
seen.add(head)
head = head.next
return False#python
1
2
3
4
5
6
7
8
9
10
11
12
13
var hasCycle = function(head) {
//js
seen = new Set()
while(head){
if(seen.has(head)){
return true
}else{
seen.add(head)
head = head.next
}
}
return false
};
方法二、双指针法:

一个指针走一格,一个指针走两格,只要快指针遇上慢指针,那么就有环

1
2
3
4
5
6
7
8
9
10
11
var hasCycle = function (head) {
let fast = head, slow = head
while (fast && fast.next) {
fast = fast.next.next
slow = slow.next
if (fast === slow) {
return true
}
}
return false
};//js
方法三、 标记法
1
2
3
4
5
6
7
8
9
10
11
const hasCycle = function(head) {
while (head) {
if (head.tag) {
return true;
}
head.tag = true;
head = head.next;
}
return false;
};

方法四以上:还有一些邪门的方法(js是这样的)
1
2
3
4
5
6
7
8
var hasCycle = function (head) {
try {
JSON.stringify(head)
} catch{
return true
}
return false
};

1.2.有环的话环在哪里(LeeCode142)

方法一、标记法
1
2
3
4
5
6
7
8
9
10
11
var detectCycle = function (head) {
while (head) {
if (head.tag) {
head.tag = null
return head
}
head.tag = true
head=head.next
}
return null
};
方法二、双指针法

双指针可以计算出相遇位置距离入口位置的距离

1
2
3
4
5
6
7
8
9
10
11
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
fast,slow=head,head
while True:
if not(fast and fast.next):return
fast,slow=fast.next.next,slow.next
if fast ==slow:break
fast =head
while fast!=slow:
fast,slow=fast.next,slow.next
return fast

2. 双向链表

解释:可以向前也能向后

2.1 基础

1
2
3
4
5
class Node():
def __init__(self,elem):
self.pre = None #前
self.elem = elem
self.next = None #后

结构和遍历方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def length(self):
#链表长度
if self.is_empty():
return 0;#判断为空
count = 0
cur = self.__head
while cur.next is not None:
count += 1
cur = cur.next
return count

def travel(self);
#遍历链表
if self.is_empty():
return
cur = self.__head
while cur.next is not None;
print(cur.elem)
cur = =cur.next
print(cur.elem)

2.2 插入元素

可想而知,双向链表插入需要知道前后内容

在第一个插入和最后一个插入的两种情况,就比较方便。但如果是在中间插入就得动四条线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
def add(self,item):
#链表头部加入
node = Node(item)
if self.is_empty():
self.__head = node#如果为空,那创建的本身就是双指针头
else:
node.next = self.__head
self.__head.pre = node.next
self.__head = node

def append(self,item):
#尾部加入
if self.is_empty():
self.__head = node#如果为空,那创建的本身就是双指针头
else:
cur = self.__head
while cur.next is not None:
cur = cur.next
cur.next = node
node.pre = cur.next

def insert(self,pos,item):
#
if pos <= 0:
self.add(item)#插入头部,调用上面函数
elif pos > self.length():
self.append(item)#插入头部,调用上面函数
else
#中间插入
node = Node(item)
count = 0
cur = self.__head
while count < (pos-1):
count += 1
cur = cur.next#找到插入位置
#进行四个链的修改
cur.next.pre = node
node.next = cur.next
cur.next =node
node.pre = cur.next


2.3 删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def remove(self,item):
#删除节点
cur = self.__head
forword = None
#遍历链表指针
while cur in not None:
#如果找到了要删除的元素
if cur.elem = item:
# 如果是头部
if cur == self.__head;
self.__head = cur.next
else:
forword.next = cur.next
return
else:
forword = cur
cur = cur.next

算法通关村第一关————链表经典问题

1、五种办法解决两个链表的第一个公共子节点问题

问题描述:输入两个链表,找出它们的第一个公共结点。

我的思路:(之前没练过算法,所以会有些笨,也叫暴力解法?),先检测链表是否符合链表要求,然后进行遍历对比,找到一个相同节点(值和存储的next都相同),返回该节点。这里应该用两个for循环,一个遍历链表1,另嵌套进去遍历链表2,然后逐个进行对比,找到相同的节点,返回该节点。

1.1、暴力解法

这个就是我想出来的办法了

像冒泡排序一样,两个for循环,一个遍历链表1,另嵌套进去遍历链表2,然后逐个进行对比,找到相同的节点,返回该节点。但时间复杂度太高,不建议使用。

1.2、hash表法

这里得对hash进行初步的理解,我们可以这样想,hash可以认为是一个用于查找的参考书,我们把一个链表加入参考书后,只需循环另一个链表,查看参考书是否有该节点,如果有,那就找到了该节点。
这里用了pythons的set集合,set集合是哈希表的实现,所以查找速度非常快。

1
2
3
4
5
6
7
8
9
10
11
12
13
# hash集合版
def getIntersectionNodebyHash( self, pHead1, pHead2):
s = set()#创建一个集合
p,q = pHead1,pHead2
while p:#遍历p
s.add(p)#把p全部都加入集合中
p = p.next
while q:#遍历q
if q in s:#把q中的内容对s一一比对,看是否有相同节点
return q
q = q.next
return None

1.3、栈法

这里我们得先理解栈的概念,这里的栈,可以理解为一个存储数据的容器,先进后出,后进先出。

可以知道的是,无论两个链表前面有多不同,它们最后都会到达一个相同的节点,所以我们可以把两个链表的节点都存入栈中,然后一一比对,找到相同的节点,返回该节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 栈版
def getIntersectionNode( self, pHead1, pHead2):
s1,s2 = [],[]#创建两个数组
p,q = pHead1,pHead2
while p:
s1.append(p)#把p全部压入s1中
p = p.next#更新“指针”
while q:
s2.append(q)
q = q.next
p,q = s1.pop(),s2.pop()#把p和q用s1代替
while p and q:#从栈顶取数
if p == q:
return p
p,q = s1.pop(),s2.pop()
return None

1.4、双指针

2、判断链表是否为回文序列

问题描述

3. 合并有序链表

问题描述

第一题 leecode318

先想写一个比较字符串

不知所谓的一些写法问题很大

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var comWords = function (words1, words2) {
let len = 0, tag1 = 0, tag2 = 0;
if (words1.length > words2.length) {
len = words1.length
tag1 = words1//大
tag2 = words2
} else {
len = words2.length
tag2 = words1
tag1 = words2 // 大
}

const set = new Set()
for (let i = 0; i < tag1.len; i++) {
set.add(tag1[i])
}
for (let j = 0; j < tag2.len; j++)
if (set.has(tag2[j])){
return 0
}
return 1
}

修改后

1
2
3
4
5
6
7
8
9
10
11
var comWords = function (words1, words2) {
const set = new Set(words1);

for (let i = 0; i < words2.length; i++) {
if (set.has(words2[i])) {
return 0;
}
}

return 1;
}

然后写一个暴力的两两组合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var maxProduct = function (words) {
max = 0
for (let i = 0; i < words.length; i++) {
for (let j = i + 1; j < words.length; j++) {
if (comWords(words[i], words[j])) {//以后看见这种写法,都应该优化掉
break;
}else {
if(words[i].length * words[j].length > max){
max = words[i].length * words[j].length;
}
}
}
}
return max;
};

出问题了,输出答案有问题

1、原因:break会直接结束循环,所以输出的都是连着一起的两个值

修改后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* @param {string[]} words
* @return {number}
*/
var comWords = function (words1, words2) {
/*
@Description: 不相同返回1
*/
const set = new Set(words1);
// console.log(set);

for (let j = 0; j < words2.length; j++) {
if (set.has(words2[j])) {
return 0;
}
}
return 1;
};

var maxProduct = function (words) {
max = 0
for (let i = 0; i < words.length; i++) {
for (let j = i + 1; j < words.length; j++) {
if (comWords(words[i], words[j])) {//不相同再进行运算比较
if(words[i].length * words[j].length > max){
max = words[i].length * words[j].length;
console.log(words[i], words[j]);
}
}
}
}
return max;
};

这样执行是通过了,但提交后说超出时间限制(暴力过头了😓)

看题解有位运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var maxProduct = function(words) {
const map = new Map();
const length = words.length;
for (let i = 0; i < length; i++) {
let mask = 0;
const word = words[i];
const wordLength = word.length;
for (let j = 0; j < wordLength; j++) {
mask |= 1 << (word[j].charCodeAt() - 'a'.charCodeAt());
}
if (wordLength > (map.get(mask) || 0)) {
map.set(mask, wordLength);
}
}
let maxProd = 0;
const maskSet = Array.from(map.keys());
for (const mask1 of maskSet) {
const wordLength1 = map.get(mask1);
for (const mask2 of maskSet) {
if ((mask1 & mask2) === 0) {
const wordLength2 = map.get(mask2);
maxProd = Math.max(maxProd, wordLength1 * wordLength2);
}
}
}
return maxProd;
};

算法总结——树

概念

定义

1
2
3
4
5
class TreeNode{
val,
TreeNode left,
TreeNode right
}

遍历

前中后序

构造

复原二叉树

实战

BFS广度优先(层次遍历)和DFS深度优先

深度更常使用

LeeCode102

JS算法总结

链表题

  1. 链表转数组

场景:要使用栈方法时

1
2
3
4
5
6
var isPalindrome = function (head) {
const vals = [];
while (head !== null) {
vals.push(head.val)//压入值,把链表转化为数组
head = head.next
}
0%