棧排序
1 ) 棧結構
遵循LIFO原則,first in last out
2 ) 排序
這裏通過插入排序來分析
通過cpp方式實現
// 這裏使用萬能頭
#include <bits/stdc++.h>
using namespace std;
stack<int> sorting(stack<int>);
int main() {
int n;
cin >> n; // 輸入總個數
stack<int> myStack;
for (int i = 0; i < n; ++i) {
int tmp;
cin >> tmp; // 逐個輸入
myStack.push(tmp);
}
stack<int> result = sorting(myStack);
vector<int> answer;
while (!result.empty()) {
answer.push_back(result.top());
result.pop();
}
// 這裏可行,auto在c++11中使用
for (auto it = answer.rbegin(); it != answer.rend(); ++it){
cout << *it << endl;
}
return 0;
}
// myStack:輸入棧,棧中的所有元素即是待排序的元素
// 返回值:輸出棧,即排序後的序列,滿足從棧底至棧頂爲升序
stack<int> sorting(stack<int> myStack) {
stack<int> result; // result存放返回值,即輸出棧
// 輸入的隨機棧是空的,那麼直接返回空的輸出
if(myStack.empty())
return result;
int tmp = myStack.top(); // 記錄下一個要插入result棧中的數,cpp語言pop後不會返回,通過pop獲取棧頂元素
myStack.pop(); // 彈出棧頂元素
// 注意這裏循環條件的邊界判斷, 輸入棧不能是空的 或
// 在輸入棧空的前提下,最後一個使輸入棧空的彈出元素tmp 小於 輸出棧的棧頂元素, 最後一次滿足循環條件
while(!myStack.empty() || (!result.empty() && tmp < result.top())) {
// 棧頂元素比臨時值小,正常push 注意邊界條件: 棧空了可以直接接push
if(result.empty() || result.top() <= tmp) {
result.push(tmp);
tmp = myStack.top();
myStack.pop(); // 更新tmp
} else {
myStack.push(result.top());
result.pop();
}
}
// 輸入棧myStack迭代空了,且其最後一個元素tmp大於輸出棧result的棧頂元素
result.push(tmp);
return result;
}
python版本
#!/usr/bin/env pypy3
# -*- coding: UTF-8 -*-
# 用於測試的數據
def sorting(stack):
'''
此方法用於棧排序的方法
'''
# 邊界檢測
if len(stack) != num:
print('wrong input, please check!')
return None;
result = []
# 檢測邊界: 初始值爲空, 返回自身
if len(stack) == 0:
return stack
# 棧頂出棧並返回棧頂元素 記錄下一個要插入result中的數
aTop = stack.pop()
# 開始循環
while (len(stack) != 0) or ((len(result) != 0 and result[len(result) - 1] > aTop)):
if (len(result) == 0 or (result[len(result) - 1] <= aTop)):
result.append(aTop) # 直接進棧
aTop = stack.pop() # 準備下一個元素
else:
stack.append(result.pop())
result.append(aTop)
return result
if __name__ == '__main__':
# input 輸入
num = int(input()) # Enter an integer length:
stackStr = input() # Enter a list of number split by a space:
stack = stackStr.split(" ") # 解析成列表
stack = [int(stack[i]) for i in range(len(stack))] # for循環,把每個字符轉成int值
# 進行排序
res = sorting(stack)
if res is None:
print('wrong answer!')
else:
# print(res)
for item in res:
print(item)
輸入樣例爲:
4
4 3 2 1
輸出樣例爲:
1
2
3
4
3 ) 思路
這裏主要思路是:準備2個棧,myStack是初始狀態隨機輸入的棧無序,result是最終的輸出棧,有序。將myStack棧頂元素與result中的所有元素做比較,找到合適的位置後將result棧中該位置後面的元素全部依次存到myStack棧頂,之後再將這些移動的元素請回result棧中,達到一次插入排序,如此循環操作,核心思想是:myStack中取出的棧頂與result棧中的棧頂元素做比較。
4 ) 說明
- sorting函數是主要的排序程序,注意各種邊界條件的判斷, 參考註釋內容
- 問題在哪裏: 來回比較效率不高,如果空間緊張且只能用棧來做,也許這是最好的辦法
- 這裏涉及一個就地(inplace)的問題, 要求算法所使用的空間只有一個單位的常數空間
- 拿cpp版本中
result.empty() || result.top() <= tmp
來說 這裏<=
換成<
是不行的- 如果存在重複元素,它會進進出出,會出現死循環
- 輸入序列中有兩個以上重複元素就會使排序出現穩定性問題,需要注意邊界問題
- 等號成立的時候直接push即可
- 關於逆序對的問題
- 例如cpp版本中
if(result.empty() || result.top() <= tmp) { ... }
這裏什麼時候result是空的,會有幾次 - 初始狀態下result一定是空的,也就是壓入第一個元素的時候
- 其他情況下,result被掏空的的情況在tmp比result最小的元素還要小
- 也就是說,如果原輸入棧myStack是有序的,如:…4321這樣 ,最多時result被掏空的次數爲輸入棧的個數
- 如果原輸入棧是有序的,這樣:1234…,那麼result在最初始化的時候只有一次爲空
- 那麼這個else分支爲執行幾次呢? 這個次數爲原輸入棧的逆序對數目
- 對於一個序列如:2967,它有兩個逆序對,首先逆序對是兩個位置(i,j)
- 如果i < j 並且 , 我們說(i,j)是一個逆序對
- 直白的說,兩個元素,相對位置關係和相對大小關係是相反的,這就是一個逆序對
- 上面2967有兩個逆序對,也就是9,6、9,7分別對應的位置,如果從0開始的話是(1,2),(1,3)
- 在這裏輸出棧result是單調遞增的, 當result棧頂元素要被彈出的時候,一定是tmp小於這個棧頂元素,而tmp在之前輸入棧myStack中是棧頂,也就是棧的最右邊
- 最右邊位置較大,而值較小,相比之下兩者則構成一個逆序對
- 例如cpp版本中