Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
wushizhenking
CS-Notes
提交
02fde522
C
CS-Notes
项目概览
wushizhenking
/
CS-Notes
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
C
CS-Notes
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
02fde522
编写于
3月 09, 2019
作者:
C
CyC2018
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
auto commit
上级
99df96b5
变更
1
隐藏空白更改
内联
并排
Showing
1 changed file
with
233 addition
and
107 deletion
+233
-107
docs/notes/算法 - 栈和队列.md
docs/notes/算法 - 栈和队列.md
+233
-107
未找到文件。
docs/notes/算法 - 栈和队列.md
浏览文件 @
02fde522
<!-- GFM-TOC -->
<!-- GFM-TOC -->
*
[
前言
](
#前言
)
*
[
栈
](
#栈
)
*
[
Quick Find
](
#quick-find
)
*
[
1. 数组实现
](
#1-数组实现
)
*
[
Quick Union
](
#quick-union
)
*
[
2. 链表实现
](
#2-链表实现
)
*
[
加权 Quick Union
](
#加权-quick-union
)
*
[
队列
](
#队列
)
*
[
路径压缩的加权 Quick Union
](
#路径压缩的加权-quick-union
)
*
[
比较
](
#比较
)
<!-- GFM-TOC -->
<!-- GFM-TOC -->
#
前言
#
栈
用于解决动态连通性问题,能动态连接两个点,并且判断两个点是否连通。
```
java
public
interface
MyStack
<
Item
>
extends
Iterable
<
Item
>
{
MyStack
<
Item
>
push
(
Item
item
);
Item
pop
()
throws
Exception
;
boolean
isEmpty
();
<div
align=
"center"
>
<img
src=
"pics/9d0a637c-6a8f-4f5a-99b9-fdcfa26793ff.png"
width=
"400"
/>
</div><br>
int
size
();
}
```
| 方法 | 描述 |
## 1. 数组实现
| :---: | :---: |
| UF(int N) | 构造一个大小为 N 的并查集 |
| void union(int p, int q) | 连接 p 和 q 节点 |
| int find(int p) | 查找 p 所在的连通分量编号 |
| boolean connected(int p, int q) | 判断 p 和 q 节点是否连通 |
```
java
```
java
public
abstract
class
UF
{
public
class
ArrayStack
<
Item
>
implements
MyStack
<
Item
>
{
protected
int
[]
id
;
// 栈元素数组,只能通过转型来创建泛型数组
private
Item
[]
a
=
(
Item
[])
new
Object
[
1
];
public
UF
(
int
N
)
{
// 元素数量
id
=
new
int
[
N
];
private
int
N
=
0
;
for
(
int
i
=
0
;
i
<
N
;
i
++)
{
id
[
i
]
=
i
;
}
@Override
public
MyStack
<
Item
>
push
(
Item
item
)
{
check
();
a
[
N
++]
=
item
;
return
this
;
}
}
public
boolean
connected
(
int
p
,
int
q
)
{
return
find
(
p
)
==
find
(
q
);
@Override
public
Item
pop
()
throws
Exception
{
if
(
isEmpty
())
{
throw
new
Exception
(
"stack is empty"
);
}
Item
item
=
a
[--
N
];
check
();
// 避免对象游离
a
[
N
]
=
null
;
return
item
;
}
}
public
abstract
int
find
(
int
p
);
public
abstract
void
union
(
int
p
,
int
q
);
private
void
check
()
{
}
```
# Quick Find
if
(
N
>=
a
.
length
)
{
resize
(
2
*
a
.
length
);
可以快速进行 find 操作,也就是可以快速判断两个节点是否连通。
}
else
if
(
N
>
0
&&
N
<=
a
.
length
/
4
)
{
resize
(
a
.
length
/
2
);
}
}
需要保证同一连通分量的所有节点的 id 值相等。
但是 union 操作代价却很高,需要将其中一个连通分量中的所有节点 id 值都修改为另一个节点的 id 值。
/**
* 调整数组大小,使得栈具有伸缩性
*/
private
void
resize
(
int
size
)
{
<div
align=
"center"
>
<img
src=
"pics/8f0cc500-5994-4c7a-91a9-62885d658662.png"
width=
"350"
/>
</div><br>
Item
[]
tmp
=
(
Item
[])
new
Object
[
size
];
```
java
for
(
int
i
=
0
;
i
<
N
;
i
++)
{
public
class
QuickFindUF
extends
UF
{
tmp
[
i
]
=
a
[
i
];
}
public
QuickFindUF
(
int
N
)
{
a
=
tmp
;
super
(
N
);
}
}
@Override
@Override
public
int
find
(
int
p
)
{
public
boolean
isEmpty
(
)
{
return
id
[
p
]
;
return
N
==
0
;
}
}
@Override
@Override
public
void
union
(
int
p
,
int
q
)
{
public
int
size
(
)
{
int
pID
=
find
(
p
)
;
return
N
;
int
qID
=
find
(
q
);
}
if
(
pID
==
qID
)
{
return
;
}
for
(
int
i
=
0
;
i
<
id
.
length
;
i
++)
{
@Override
if
(
id
[
i
]
==
pID
)
{
public
Iterator
<
Item
>
iterator
()
{
id
[
i
]
=
qID
;
// 返回逆序遍历的迭代器
return
new
Iterator
<
Item
>()
{
private
int
i
=
N
;
@Override
public
boolean
hasNext
()
{
return
i
>
0
;
}
}
}
@Override
public
Item
next
()
{
return
a
[--
i
];
}
};
}
}
}
}
```
```
#
Quick Union
#
# 2. 链表实现
可以快速进行 union 操作,只需要修改一个节点的 id 值即可
。
需要使用链表的头插法来实现,因为头插法中最后压入栈的元素在链表的开头,它的 next 指针指向前一个压入栈的元素,在弹出元素时就可以通过 next 指针遍历到前一个压入栈的元素从而让这个元素成为新的栈顶元素
。
但是 find 操作开销很大,因为同一个连通分量的节点 id 值不同,id 值只是用来指向另一个节点。因此需要一直向上查找操作,直到找到最上层的节点。
```
java
public
class
ListStack
<
Item
>
implements
MyStack
<
Item
>
{
<div
align=
"center"
>
<img
src=
"pics/5d4a5181-65fb-4bf2-a9c6-899cab534b44.png"
width=
"350"
/>
</div><br>
private
Node
top
=
null
;
private
int
N
=
0
;
```
java
public
class
QuickUnionUF
extends
UF
{
public
QuickUnionUF
(
int
N
)
{
private
class
Node
{
super
(
N
);
Item
item
;
Node
next
;
}
}
@Override
@Override
public
int
find
(
int
p
)
{
public
MyStack
<
Item
>
push
(
Item
item
)
{
while
(
p
!=
id
[
p
])
{
p
=
id
[
p
];
Node
newTop
=
new
Node
();
}
return
p
;
newTop
.
item
=
item
;
newTop
.
next
=
top
;
top
=
newTop
;
N
++;
return
this
;
}
}
@Override
@Override
public
void
union
(
int
p
,
int
q
)
{
public
Item
pop
()
throws
Exception
{
int
pRoot
=
find
(
p
);
int
qRoot
=
find
(
q
);
if
(
pRoot
!=
qRoot
)
{
if
(
isEmpty
()
)
{
id
[
pRoot
]
=
qRoot
;
throw
new
Exception
(
"stack is empty"
)
;
}
}
Item
item
=
top
.
item
;
top
=
top
.
next
;
N
--;
return
item
;
}
@Override
public
boolean
isEmpty
()
{
return
N
==
0
;
}
@Override
public
int
size
()
{
return
N
;
}
@Override
public
Iterator
<
Item
>
iterator
()
{
return
new
Iterator
<
Item
>()
{
private
Node
cur
=
top
;
@Override
public
boolean
hasNext
()
{
return
cur
!=
null
;
}
@Override
public
Item
next
()
{
Item
item
=
cur
.
item
;
cur
=
cur
.
next
;
return
item
;
}
};
}
}
}
}
```
```
这种方法可以快速进行 union 操作,但是 find 操作和树高成正比,最坏的情况下树的高度为节点的数目。
# 队列
<div
align=
"center"
>
<img
src=
"pics/bfbb11e2-d208-4efa-b97b-24cd40467cd8.png"
width=
"130"
/>
</div><br>
下面是队列的链表实现,需要维护 first 和 last 节点指针,分别指向队首和队尾。
# 加权 Quick Union
这里需要考虑 first 和 last 指针哪个作为链表的开头。因为出队列操作需要让队首元素的下一个元素成为队首,所以需要容易获取下一个元素,而链表的头部节点的 next 指针指向下一个元素,因此可以让 first 指针链表的开头。
为了解决 quick-union 的树通常会很高的问题,加权 quick-union 在 union 操作时会让较小的树连接较大的树上面。
```
java
public
interface
MyQueue
<
Item
>
extends
Iterable
<
Item
>
{
int
size
();
理论研究证明,加权 quick-union 算法构造的树深度最多不超过 logN。
boolean
isEmpty
();
<div
align=
"center"
>
<img
src=
"pics/a4c17d43-fa5e-4935-b74e-147e7f7e782c.png"
width=
"170"
/>
</div><br>
MyQueue
<
Item
>
add
(
Item
item
);
Item
remove
()
throws
Exception
;
}
```
```
java
```
java
public
class
WeightedQuickUnionUF
extends
UF
{
public
class
ListQueue
<
Item
>
implements
MyQueue
<
Item
>
{
// 保存节点的数量信息
private
Node
first
;
private
int
[]
sz
;
private
Node
last
;
int
N
=
0
;
public
WeightedQuickUnionUF
(
int
N
)
{
private
class
Node
{
super
(
N
);
Item
item
;
this
.
sz
=
new
int
[
N
];
Node
next
;
for
(
int
i
=
0
;
i
<
N
;
i
++)
{
this
.
sz
[
i
]
=
1
;
}
}
}
@Override
@Override
public
int
find
(
int
p
)
{
public
boolean
isEmpty
()
{
while
(
p
!=
id
[
p
])
{
return
N
==
0
;
p
=
id
[
p
];
}
return
p
;
}
}
@Override
@Override
public
void
union
(
int
p
,
int
q
)
{
public
int
size
()
{
return
N
;
}
int
i
=
find
(
p
);
int
j
=
find
(
q
);
if
(
i
==
j
)
return
;
@Override
public
MyQueue
<
Item
>
add
(
Item
item
)
{
Node
newNode
=
new
Node
();
newNode
.
item
=
item
;
newNode
.
next
=
null
;
if
(
sz
[
i
]
<
sz
[
j
]
)
{
if
(
isEmpty
()
)
{
id
[
i
]
=
j
;
last
=
newNode
;
sz
[
j
]
+=
sz
[
i
]
;
first
=
newNode
;
}
else
{
}
else
{
id
[
j
]
=
i
;
last
.
next
=
newNode
;
sz
[
i
]
+=
sz
[
j
]
;
last
=
newNode
;
}
}
N
++;
return
this
;
}
}
}
```
# 路径压缩的加权 Quick Union
在检查节点的同时将它们直接链接到根节点,只需要在 find 中添加一个循环即可。
@Override
public
Item
remove
()
throws
Exception
{
if
(
isEmpty
())
{
throw
new
Exception
(
"queue is empty"
);
}
Node
node
=
first
;
first
=
first
.
next
;
N
--;
if
(
isEmpty
())
{
last
=
null
;
}
return
node
.
item
;
}
# 比较
| 算法 | union | find |
@Override
| :---: | :---: | :---: |
public
Iterator
<
Item
>
iterator
()
{
| Quick Find | N | 1 |
| Quick Union | 树高 | 树高 |
return
new
Iterator
<
Item
>()
{
| 加权 Quick Union | logN | logN |
| 路径压缩的加权 Quick Union | 非常接近 1 | 非常接近 1 |
Node
cur
=
first
;
@Override
public
boolean
hasNext
()
{
return
cur
!=
null
;
}
@Override
public
Item
next
()
{
Item
item
=
cur
.
item
;
cur
=
cur
.
next
;
return
item
;
}
};
}
}
```
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录