常見資料結構(一)-棧,佇列,堆,雜湊表
阿新 • • 發佈:2018-11-23
轉載:https://blog.csdn.net/u013063153/article/details/54667361?locationNum=8&fps=1
寫在前面
- 本文所有圖片均截圖自coursera上普林斯頓的課程《Algorithms, Part I》中的Slides
- 相關命題的證明可參考《演算法(第4版)》
- 原始碼可在官網下載,也可以在我的github倉庫 algorithms-learning下載,已經使用maven構建
- 倉庫下載:
git clone [email protected]
Stacks(棧)
LIFO(後進先出):last in first out.
- 使用linked-list實現
儲存指向第一個節點的指標,每次從前面插入/刪除節點。
以字串棧為例,示例程式碼:
-
public class LinkedStackOfStrings {
-
private
Node
first
=
null
;
-
-
private
class
Node
{
-
String
item
;
-
Node
next
;
-
}
-
-
public
boolean
isEmpty
()
{
-
return
first
==
null
;
-
}
-
-
public
void
push
(
String
item
)
{
-
Node
oldfirst
=
first
;
-
first
=
new
Node
();
-
first
.
item
=
item
;
-
first
.
next
=
oldfirst
;
-
}
-
-
public
String
pop
()
{
-
String
item
=
first
.
item
;
-
first
=
first
.
next
;
-
return
item
;
-
}
-
}
-
- 使用陣列實現
使用陣列來儲存棧中的項
-
public class FixedCapacityStackOfStrings {
-
private
String
[]
s
;
-
private
int
N
=
0
;
-
-
public
FixedCapacityStackOfStrings
(
int
capacity
)
{
-
s
=
new
String
[
capacity
];
-
}
-
-
public
boolean
isEmpty
()
{
-
return
N
==
0
;
-
}
-
-
public
void
push
(
String
item
)
{
-
s
[
N
++]
=
item
;
-
}
-
-
public
String
pop
()
{
-
String
item
=
s
[--
N
];
-
s
[
N
]
=
null
;
-
return
item
;
-
}
-
}
-
上面的實現會有幾個問題:
- 從空棧pop會丟擲異常
- 插入元素過多會超出陣列上界
這裡重點解決第二個問題,resizing arrays.一個可行的方案是: 當陣列滿的時候,陣列大小加倍;當陣列是1/4滿的時候,陣列大小減半。 這裡不是在陣列半滿時削減size,這樣可以避免陣列在將滿未滿的臨界點多次push-pop-push-pop操作造成大量的陣列拷貝操作。
插入N個元素,N + (2 + 4 + 8 + ... + N) ~ 3N
。
- N:1 array access per push
- (2 + 4 + 8 + … + N):k array accesses to double to size k (ignoring cost to create new array)
由於resize操作不是經常發生,所以均攤下來,平均每次push/pop操作的還是常量時間(constant amortized time).
Queues(佇列)
FIFO(先進先出):first in first out.
- 使用linked-list實現
儲存指向首尾節點的指標,每次從連結串列尾插入,從連結串列頭刪除。
-
public class LinkedQueueOfStrings {
-
private
Node
first
,
last
;
-
-
private
class
Node
{
-
/* same as in StackOfStrings */
-
}
-
-
public
boolean
isEmpty
()
{
-
return
first
==
null
;
-
}
-
-
public
void
enqueue
(
String
item
)
{
-
Node
oldlast
=
last
;
-
last
=
new
Node
();
-
last
.
item
=
item
;
-
last
.
next
=
null
;
-
if
(
isEmpty
())
{
-
first
=
last
;
-
}
else
{
-
oldlast
.
next
=
last
;
-
}
-
}
-
-
public
String
dequeue
()
{
-
String
item
=
first
.
item
;
-
first
=
first
.
next
;
-
if
(
isEmpty
())
last
=
null
;
-
return
item
;
-
}
-
}
-
- 使用陣列實現
-
・Use array q[] to store items in queue.
-
・enqueue(): add new item at
q[tail].
-
・dequeue(): remove item from
q[head].
-
・Update head
and tail modulo the capacity.
-
・Add resizing array.
-
Priority Queues
Collections. Insert and delete items.
- Stack. Remove the item most recently added.
- Queue. Remove the item least recently added. Randomized queue. Remove a random item.
- Priority queue. Remove the largest (or smallest) item.
unordered array 實現
-
public class UnorderedMaxPQ<Key extends Comparable<Key>> {
-
private
Key
[]
pq
;
// pq[i] = ith element on pq
-
private
int
N
;
// number of elements on pq
-
-
public
UnorderedMaxPQ
(
int
capacity
)
{
-
pq
=
(
Key
[])
new
Comparable
[
capacity
];
-
}
-
-
public
boolean
isEmpty
()
{
-
return
N
==
0
;
-
}
-
-
public
void
insert
(
Key
x
)
{
-
pq
[
N
++]
=
x
;
-
}
-
-
public
Key
delMax
()
{
-
int
max
=
0
;
-
for
(
int
i
=
1
;
i
<
N
;
i
++)
-
if
(
less
(
max
,
i
))
max
=
i
;
-
-
exch
(
max
,
N
-
1
);
-
return
pq
[--
N
];
-
}
-
}
-
Binary Heaps(二叉堆)
使用陣列來表示一個二叉堆。根節點索引從1開始。索引對應在樹中的位置,最大的鍵值是a[1],同時也是二叉樹的根節點。
- Parent’s key no smaller than children’s keys
- Indices start at 1.
- Parent of node at k is at k/2.
- Children of node at k are at 2k and 2k+1.
上浮和下沉
有兩種情況會觸發節點移動:
- 子節點的鍵值變為比父節點大
- 父節點的鍵值變為比子節點(一個或兩個)小
而 要消除這種違反最大堆定義的結構,就需要進行節點移動和交換, 使之滿足父節點鍵值不小於兩個子節點 。對應的操作分別是 上浮 和 下沉
- 上浮:子節點key比父節點大
- Exchange key in child with key in parent.
- Repeat until heap order restored.
- 下沉:父節點key比子節點(one or both)小
- Exchange key in parent with key in larger child.
- Repeat until heap order restored
-
/* 上浮 */
-
private
void
swim
(
int
k
)
{
-
while
(
k
>
1
&&
less
(
k
/
2
,
k
))
{
-
exch
(
k
,
k
/
2
);
-
k
=
k
/
2
;
-
}
-
}
-
-
/* 下沉 */
-
private
void
sink
(
int
k
)
{
-
while
(
2
*
k
<=
N
)
{
-
int
j
=
2
*
k
;
-
if
(
j
<
N
&&
less
(
j
,
j
+
1
))
j
++;
-
if
(!
less
(
k
,
j
))
break
;
-
exch
(
k
,
j
);
-
k
=