棧(Stack),輕松解決數制轉換和括號匹配問題!

http://data.biancheng.net/view/9.html

棧,線性表的一種特殊的存儲結構。與學習過的線性表的不同之處在于棧只能從表的固定一端對數據進行插入和刪除操作,另一端是封死的。

圖1 棧結構示意圖
由于棧只有一邊開口存取數據,稱開口的那一端為“棧頂”,封死的那一端為“棧底”(類似于盛水的木桶,從哪進去的最后還得從哪出來)。

棧的“先進后出”原則

使用棧存儲數據元素,對數據元素的 “存” “取” 有嚴格的規定:數據按一定的順序存儲到棧中,當需要調取棧中某數據元素時,需要將在該數據元素之后進棧的先出棧,該數據元素才能從棧中提取出來。

如圖 1 ,棧中存放了 4 個數據元素,進棧的順序是 A 先進棧,然后 B 進,然后 C 進,最后 D 進棧;當需要調取 A 時,首先 D 出棧,然后 C 出棧,然后 B 出棧,最后 A 才能出棧被調用。

就好比只有一個門的車庫(每次僅允許一輛車通過),每輛車好比一個數據元素,只有離門最近的車先開出來,里邊的車才能出來;最里邊的車是最先開進去的,注定要最后出來。

棧操作數據元素的方法

棧操作數據元素只有兩種動作:
  1. 數據元素用棧的數據結構存儲起來,稱為“入棧”,也叫“壓棧”
  2. 數據元素由于某種原因需要從棧結構中提取出來,稱為“出棧”,也叫“彈棧”

棧的兩種表示方式

既然棧也是線性表,那么它就同樣有線性表的兩種表示形式: 順序棧 ?和? 鏈式棧 (簡稱 “鏈棧” )。
兩者的區別在于存儲的數據元素在物理結構上是否是相互緊挨著的。順序棧存儲元素預先申請連續的存儲單元;鏈棧需要即申請,數據元素不緊挨著。

棧的“上溢”和“下溢”

棧存儲結構調取棧中數據元素時,要避免出現“上溢”“下溢”的情況:

“上溢”:在棧已經存滿數據元素的情況下,如果繼續向棧內存入數據,棧存儲就會出錯。

“下溢”:在棧內為空的狀態下,如果對棧繼續進行取數據的操作,就會出錯。

棧的“上溢”和“下溢”,可以總結為:棧滿還存會“上溢”,棧空再取會“下溢”。

對于棧的兩種表示方式來說,順序棧兩種情況都有可能發生;而鏈棧由于“隨時需要,隨時申請空間”的存儲結構,不會出現“上溢”的情況。

順序棧

順序棧的實現采用的是數組。

在順序棧中設定一個隨時指向棧頂元素的變量(一般命名為 top ),當 top 的值為 -1 時,說明數組中沒有數據,即棧中沒有數據元素,為 “空棧” ;只要數據元素進棧,top 就加 1 ;數據元素出棧, top 就減 1 。

例如,使用順序棧的存儲結構將(’a’,’b’,’c’,’d’)四個元素逐個壓棧并輸出棧頂元素。

實現代碼:
  1. #include <stdio.h>
  2. //元素elem進棧
  3. int push(char* a,int top,char elem){
  4. a[++top]=elem;
  5. return top;
  6. }
  7. //數據元素出棧
  8. int pop(char * a,int top){
  9. if (top==-1) {
  10. printf("空棧");
  11. return -1;
  12. }
  13. printf("彈棧元素:%c\n",a[top]);
  14. top--;
  15. return top;
  16. }
  17. int main() {
  18. char a[100];
  19. int top=-1;
  20. top=push(a, top, 'a');
  21. top=push(a, top, 'b');
  22. top=push(a, top, 'c');
  23. top=push(a, top, 'd');
  24. top=pop(a, top);
  25. top=pop(a, top);
  26. top=pop(a, top);
  27. top=pop(a, top);
  28. top=pop(a, top);
  29. return 0;
  30. }
輸出結果:
彈棧元素:d
彈棧元素:c
彈棧元素:b
彈棧元素:a
空棧

鏈棧

鏈棧,用線性表的鏈式存儲結構實現。

鏈棧一般不需要創建頭結點,頭結點會增加程序的復雜性,只需要創建一個頭指針就可以了。

用鏈表表示棧時,用鏈表頭結點的一端作為棧的棧頂端,這樣做的好處是當數據元素壓棧或者彈棧時,直接使用頭指針就可以完成,不需要增設額外的指針。

例如,用鏈棧實現將(’a’,’b’,’c’,’d’)四個數據元素壓棧,再依次彈棧:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. typedef struct lineStack{
  4. char data;
  5. struct lineStack * next;
  6. }lineStack;
  7. lineStack* push(lineStack * stack,char a){
  8. lineStack * line=(lineStack*)malloc(sizeof(lineStack));
  9. line->data=a;
  10. line->next=stack;
  11. stack=line;
  12. return stack;
  13. }
  14. lineStack * pop(lineStack * stack){
  15. if (stack) {
  16. lineStack * p=stack;
  17. stack=stack->next;
  18. printf("彈棧元素:%c ",p->data);
  19. if (stack) {
  20. printf("棧頂元素:%c\n",stack->data);
  21. }else{
  22. printf("棧已空\n");
  23. }
  24. free(p);
  25. }else{
  26. printf("棧內沒有元素");
  27. return stack;
  28. }
  29. return stack;
  30. }
  31. int main() {
  32. lineStack * stack=NULL;
  33. stack=push(stack, 'a');
  34. stack=push(stack, 'b');
  35. stack=push(stack, 'c');
  36. stack=push(stack, 'd');
  37. stack=pop(stack);
  38. stack=pop(stack);
  39. stack=pop(stack);
  40. stack=pop(stack);
  41. stack=pop(stack);
  42. return 0;
  43. }
輸出結果:
彈棧元素:d 棧頂元素:c
彈棧元素:c 棧頂元素:b
彈棧元素:b 棧頂元素:a
彈棧元素:a 棧已空
棧內沒有元素

棧的實際應用

實際生活中,使用手機(無論是 iPhone 還是安卓)時,屏幕頁面的跳轉使用的就是棧。當你選擇換一個頁面瀏覽時,之前的頁面被存儲到棧中;每次做回退操作時,會回到上一個頁面,這是進棧的頁面逐個出棧的效果。

另外,在求 n! 時,有一種算法會涉及到函數的遞歸(函數自己調用自己的過程),這個過程的底層實現就用到了棧。

除此之外,數制轉換和括號匹配問題,也可以用棧來解決。

數制轉換

實際生活中主要使用 10 進制,而計算機只認識 2 進制,在解決數制轉換的問題上使用棧結構會事半功倍。

例如,將十進制轉化成二進制,代碼實現:
  1. #include <stdio.h>
  2. int top=-1;//top變量時刻表示棧頂元素所在位置
  3. void push(int * a,int elem){
  4. a[++top]=elem;
  5. }
  6. void pop(int * a){
  7. if (top==-1) {
  8. return ;
  9. }
  10. printf("%d",a[top]);
  11. top--;
  12. }
  13. int main() {
  14. int a[30];
  15. int div = 0;
  16. scanf("%d",&div);
  17. while (div/2) {
  18. push(a,div%2);
  19. div/=2;
  20. }
  21. push(a,div%2);
  22. while (top!=-1) {
  23. pop(a);
  24. }
  25. }
運行結果:
15
1111
2 進制和 10 進制之間的轉換方法為:15 = 1*20?+ 1*21?+ 1*22?+ 1*23

括號匹配的檢查

在編寫代碼的時候,經常會用到兩種括號:圓括號 “()” 和大括號 “{}” 。不管使用哪種括號,程序編譯沒有問題的一個考慮因素就是所使用的括號是否能夠匹配上,在編寫程序時,括號可以嵌套,即: “({()})” 這種形式,但 “({)” 或者 “({}” 都不符合要求。

編寫程序判斷括號匹配問題的時候,使用棧結構會很容易:
  • 如果碰到的是左圓括號或者左大括號,直接壓棧;
  • 如果碰到的是右圓括號或者右大括號,就直接和棧頂元素配對:如果匹配,棧頂元素彈棧;反之,括號不匹配;
實現代碼:
  1. #include <stdio.h>
  2. #include <string.h>
  3. int top=-1;//top變量時刻表示棧頂元素所在位置
  4. void push(char * a,int elem){
  5. a[++top]=elem;
  6. }
  7. void pop(char* a){
  8. if (top==-1) {
  9. return ;
  10. }
  11. top--;
  12. }
  13. char visit(char * a){
  14. //調取棧頂元素,不等于彈棧,如果棧為空,為使程序不發生錯誤,返回空字符
  15. if (top!=-1) {
  16. return a[top];
  17. }else{
  18. return ' ';
  19. }
  20. }
  21. int main() {
  22. char a[30];
  23. char bracket[100];
  24. scanf("%s",bracket);
  25. int length=(int)strlen(bracket);
  26. for (int i=0; i<length; i++) {
  27. //如果是左括號,直接壓棧
  28. if (bracket[i]=='('||bracket[i]=='{') {
  29. push(a, bracket[i]);
  30. }else{
  31. //判斷與棧頂元素是否匹配,如果匹配,棧頂元素彈棧,程序繼續運行;否則,發現括號不匹配,輸出結果直接退出
  32. if (bracket[i]==')') {
  33. if (visit(a)=='(') {
  34. pop(a);
  35. }else{
  36. printf("括號不匹配");
  37. return 0;
  38. }
  39. }else{
  40. if (visit(a)=='{') {
  41. pop(a);
  42. }else{
  43. printf("括號不匹配");
  44. return 0;
  45. }
  46. }
  47. }
  48. }
  49. //如果所有括號匹配完成,棧內為空,說明所有括號全部匹配成功
  50. if (top!=-1) {
  51. printf("括號不匹配");
  52. }else{
  53. printf("括號匹配");
  54. }
  55. }
運行結果:
({()})
括號匹配

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/384225.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/384225.shtml
英文地址,請注明出處:http://en.pswp.cn/news/384225.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

第一章 TCP/IP協議族

一、協議族體系結構 TCP/IP協議族分為四層協議系統&#xff0c;自底向下分別為數據鏈路層、網絡層、傳輸層、應用層。 數據鏈路層常用ARP&#xff08;地址解析協議&#xff09;和RARP&#xff08;逆地址解析協議&#xff09;。在網絡層使用IP尋址&#xff0c;而在數據鏈路層使用…

二分(三分)+快速冪

之前學習的二分&#xff0c;現在感覺突然理解許多&#xff0c;補一下總結 首先&#xff0c;二分能夠解決什么樣的問題呢&#xff0c;個人認為&#xff0c;二分能夠快速解決已經知道答案范圍&#xff08;線性&#xff09;但是不知道確切答案的問題&#xff0c;例如在一個有序序列…

pthread_cleanup_push與pthread_cleanup_pop的目的 作用

http://blog.csdn.net/slj_win/article/details/7267483 首先你必須知道pthread_cleanup_push與pthread_cleanup_pop的目的(作用)是什么。 比如thread1: 執行 pthread_mutex_lock(&mutex); //一些會阻塞程序運行的調用&#xff0c;比如套接字的accept&#xff0c;等待客…

動態規劃淺談

接觸動態規劃這么久了&#xff0c;簡單談一下自己對動態規劃的理解。 動態規劃名字聽起來好像比比較高大上&#xff0c;可是事實上&#xff0c;人家就是比較高大上。&#xff08;抖個機靈&#xff09; 剛開始接觸動態規劃的時候覺得好可怕&#xff0c;這么復雜的問題我怎么能想…

Linux多線程——使用信號量同步線程

http://blog.csdn.net/ljianhui/article/details/10813469/ 信號量、同步這些名詞在進程間通信時就已經說過&#xff0c;在這里它們的意思是相同的&#xff0c;只不過是同步的對象不同而已。但是下面介紹的信號量的接口是用于線程的信號量&#xff0c;注意不要跟用于進程間通信…

linux下安裝erlang

1.安裝Erlang編譯依賴: yum -y install gcc glibc-devel make ncurses-devel openssl-devel xmlto perl wget 2.下載Erlang&#xff1a; wget http://www.erlang.org/download/otp_src_19.3.tar.gz 3.解壓并安裝 tar -xzvf otp_src_19.3.tar.gz cd otp_src_19.3 ./configure --…

Linux 線程同步的三種方法

http://blog.csdn.net/zsf8701/article/details/7844316 線程的最大特點是資源的共享性&#xff0c;但資源共享中的同步問題是多線程編程的難點。linux下提供了多種方式來處理線程同步&#xff0c;最常用的是互斥鎖、條件變量和信號量。 一、互斥鎖(mutex) 通過鎖機制實現線程…

Elixir特性

iex 退出&#xff1a;Ctrl-C 或Ctrl-G再輸入q 回車。 幫助文檔&#xff1a;h 查看輔函數列表 h IO 查看IO模塊幫助 h IO.puts 查看IO模塊中的puts函數的文檔 編譯和運行&#xff1a;創建一個hello.exs的文件。IO.puts "hello world"    //輸出hello world 使用el…

Elixir基礎

值類型 整數&#xff0c;包括十進制&#xff08;1234&#xff09;、十六進制&#xff08;0xcafe&#xff09;、八進制&#xff08;0o765&#xff09;和二進制&#xff08;0b1010&#xff09; 浮點數 原子&#xff0c;原子是常量&#xff0c;用于表現某些東西的名字&#xff0c;…

C++11新特性之八——函數對象function

http://www.cnblogs.com/yyxt/p/3987717.html 詳細請看《C Primer plus》(第六版中文版) http://www.cnblogs.com/lvpengms/archive/2011/02/21/1960078.html 備注&#xff1a; 函數對象&#xff1a; 盡管函數指針被廣泛用于實現函數回調&#xff0c;但C還提供了一個重要的實現…

分塊思想

今天學習了一個算法&#xff08;這個應該叫做算法吧&#xff1f;&#xff09;叫做分塊&#xff08;和莫隊&#xff0c;但是莫隊還沒有搞懂&#xff0c;搞懂再來寫吧&#xff09; 聽起來很高級&#xff0c;蒟蒻表示瑟瑟發抖。但是學完發現怎么那么像是一種變相的暴力呢。 分塊思…

從零開始學C++之STL(八):函數對象、 函數對象與容器、函數對象與算法

http://blog.csdn.net/jnu_simba/article/details/9500219 一、函數對象 1、函數對象&#xff08;function object&#xff09;也稱為仿函數&#xff08;functor&#xff09; 2、一個行為類似函數的對象&#xff0c;它可以沒有參數&#xff0c;也可以帶有若干參數。 3、任何重載…

樹狀數組初步理解

學習樹狀數組已經兩周了&#xff0c;之前偷懶一直沒有寫&#xff0c;趕緊補上防止自己忘記&#xff08;雖然好像已經忘得差不多了&#xff09;。 作為一種經常處理區間問題的數據結構&#xff0c;它和線段樹、分塊一樣&#xff0c;核心就是將區間分成許多個小區間然后通過對大區…

命名函數

函數體是代碼塊 代碼塊do...end是一種表達式的組織方式。 # ./times.exs下defmodule Times dodef doule(n) don * 2end end 函數調用與模式匹配 代碼如下&#xff1a; # ./factorial.exs    計算階層 defmodule Factorial dodef of(0), do: 1          #終止條件…

STL運用的C++技術(6)——函數對象

http://blog.csdn.net/wuzhekai1985/article/details/6658940?_t_t_t0.20427969420870595 STL是C標準庫的重要組成部分之一&#xff0c;它不僅是一個可復用的組件庫&#xff0c;更是一個包含算法與數據結構的軟件框架&#xff0c;同時也是C泛型編程的很好例子。STL中運用了許多…

列表與遞歸

頭部和尾部 [head | tail ] [1] #head 1 tail [] [head | tail ] [1, 2, 3] #head 1 tail [2, 3] [head | tail ] [] #報錯 創建映射函數 我們可以使用一個函數來處理列表中的各個元素&#xff0c;如此可以接受更加復雜的處理&#xff0c;也可以…

優先隊列小結

不像棧和隊列&#xff0c;雖然STL有較好實現但是我們自己也可以很方便的實現&#xff0c;優先隊列自己實現起來就比較復雜&#xff0c;比較浪費時間&#xff08;而且自己目前也不會233&#xff09;而優先隊列因為其較好的特性經常被使用&#xff0c;因此對它的熟練掌握是做題的…

字典:散列表、散列字典、關鍵字列表、集合與結構體

字典 散列表和散列字典都實現了Dict的行為。Keyword模塊也基本實現了&#xff0c;不同之處在于它支持重復鍵。 Eunm.into可以將一種類型的收集映射轉化成另一種。 defmodule Sum dodef values(dict) dodict |> Dict.values |> Enum.sumend endhd [ one: 1, two: 2, thre…

C++11 學習筆記 lambda表達式

http://blog.csdn.net/fjzpdkf/article/details/50249287 lambda表達式是C11最重要也最常用的一個特性之一。lambda來源于函數式編程的概念&#xff0c;也是現代編程語言的一個特點。 一.函數式編程簡介 定義&#xff1a;簡單說&#xff0c;“函數式編程”是一種“編程范式”。…

Cutting Codeforces Round #493 (Div. 2)

Cutting There are a lot of things which could be cut — trees, paper, “the rope”. In this problem you are going to cut a sequence of integers. There is a sequence of integers, which contains the equal number of even and odd numbers. Given a limited bud…