日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

《大话数据结构》读书笔记-串

發(fā)布時間:2025/4/5 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《大话数据结构》读书笔记-串 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

寫在前面:本文僅供個人學習使用。《大話數(shù)據(jù)結(jié)構(gòu)》通俗易懂,適合整體做筆記輸出,構(gòu)建體系。并且文中很多圖片來源于該書。

文章目錄

    • 5.2 串的定義
    • 5.3串的比較
    • 5.4串的抽象數(shù)據(jù)類型
    • 5.5 串的存儲結(jié)構(gòu)
      • 5.5.1 串的順序存儲結(jié)構(gòu)
      • 5.5.2 串的鏈式存儲結(jié)構(gòu)
    • 5.6樸素的模式匹配算法
    • 5.7 KMP模式匹配算法
      • 5.7.1 KMP模式匹配算法原理
      • 5.7.2 next數(shù)組值推導
      • 5.7.3 KMP模式匹配算法實現(xiàn)
      • 5.7.4 KMP模式匹配算法改進
      • 5.7.5 nextval數(shù)組值推導

串:串(string)是由零個或多個字符組成的有限序列,又名叫字符串。

5.2 串的定義

早先的計算機在被發(fā)明時,主要作用是做一些科學和工程的計算工作,也就是現(xiàn)在我們理解的計算器,只不過它比小小的計算器功能更強大、速度更快一些。后來發(fā)現(xiàn),在計算機上作非數(shù)值處理的工作越來越多,使得我們不得不需要引入對字符的處理。 于是就有了字符串的概念。

比如我們現(xiàn)在常用的搜索引擎,當我們在文本框中輸入“數(shù)據(jù)”時,它已經(jīng)把我們想要的“數(shù)據(jù)結(jié)構(gòu)“列在下面了。顯然這里網(wǎng)站作了一個字符串查找匹配的工作,如圖所示。


今天我們就來研究”串“這樣的數(shù)據(jù)結(jié)構(gòu),先來看定義。串(string) 是由零個或多個字符組成的有限序列,又名叫字符串。

一般記為s="a1a2...an(n≥0)"s="a_1a_2...a_n(n≥0)"s="a1?a2?...an?(n0)",其中,s是串的名稱,用雙引號括起來的字符序列是串的值,注意引號不是串的內(nèi)容。aia_iai?可以是字母、數(shù)字或其他字符,i就是該字符在串中的位置。串中的字符數(shù)目n稱為串的長度,定義中談到”有限“是指長度n是一個有限的數(shù)值。零個字符的串稱為空串(null string),它的長度為零,可以直接用兩雙引號”""“表示,也可以用希臘字母Φ\PhiΦ來表示。所謂的序列,說明串的相鄰字符之間具有前驅(qū)和后繼的關系。

還有一些概念需要解釋。

空格串,是只包含空格的串。注意它與空串的區(qū)別,空格串是有內(nèi)容有長度的,而且可以不止一個空格。
子串與主串,串中任意個數(shù)的連續(xù)字符組成的子序列稱為該串的子串,相應地,包含子串的串稱為主串。

子串在主串中的位置就是子串的第一個字符在主串中的序號。

5.3串的比較

兩個數(shù)字,很容易比較大小。2比1大,這完全正確,可是兩個字符串如何比較? 比如”silly“ ”stupid“ 這樣的同樣表達”愚蠢的“的單詞字符串,它們在計算機中的大小其實取決于它們挨個字母的前后順序。 它們的第一個字母都是”s“,我們認為不存在大小差異,而第二個字母,由于”i“字母比”t“字母要靠前,所以”i“<“t”,于是我們說”silly“<“stupid”。

事實上,串的比較是通過組成串的字符之間的編碼來進行的,而字符的編碼指的是字符在對應字符集中的序號。

計算機中常用的字符使用ASCII編碼,更準確一點,由7位二進制數(shù)表示一個字符,總共可以表示128個字符。后來發(fā)現(xiàn)由于一些特殊字符的出現(xiàn),128個不夠用,于是擴展ASCII碼由8位二進制數(shù)表示一個字符,總共可以表示256個字符,這已經(jīng)足夠滿足以英語位主的語言和特殊符號進行輸入、存儲、輸出等操作的字符需要了。 可是,單我們國家就有除漢族外的滿、回、藏、蒙古等多個少數(shù)民族文字,換作全世界估計要有成百上千種語言和文字,顯然這256個字符是不夠的,因此后來就有了Unicode編碼,比較常用的是由16位的二進制數(shù)表示一個字符,這樣總共就可以表示2162^{16}216個字符,約是65萬多個字符,足夠表示世界上所有語言的所有字符了。當然,為了和ASCII兼容,Unicode的前256個字符與ASCII碼完全相同。

所以,如果我們要在C語言中比較兩個串是否相等,必須是它們串的長度以及它們各自對應位置的字符都相等時,才算是相等。 即給定兩個串:s="a1a2...an"s="a_1a_2...a_n"s="a1?a2?...an?"t="b1b2...bm"t="b_1b_2...b_m"t="b1?b2?...bm?",當且僅當n=m,且a1=b1,a2=b2,...an=bma_1=b_1,a_2=b_2,...a_n=b_ma1?=b1?,a2?=b2?,...an?=bm?時,我們認為s=t。

那么對于兩個不相等的串,如何比較它們的大小呢?我們這樣定義:
給定兩個串:s="a1a2...an"s="a_1a_2...a_n"s="a1?a2?...an?"t="b1b2...bm"t="b_1b_2...b_m"t="b1?b2?...bm?",當滿足以下條件之一時,有s<t.

  • n<m,且ai=bi(i=1,2,...n)a_i=b_i(i=1,2,...n)ai?=bi?(i=1,2,...n) 例如 當s=“hap”,t=“happy”,就有s<t.因為t比s多出了兩個字母。
  • 存在某個k≤min(m,n),使得ai=bi(i=1,2,...,k?1),而ak<bk。a_i=b_i(i=1,2,...,k-1),而a_k<b_k。ai?=bi?(i=1,2,...,k?1),ak?<bk?例如當s=“happen”,t=“happy”,因為兩串的前4個字母均相同,而兩串第5(k值)個字母,字母e的ASCII碼是101,而字母y的ASCII碼是121,顯然e<y,所以s<t.
  • 說白了,就是字典序,即字典中排列的順序。

    5.4串的抽象數(shù)據(jù)類型

    串的邏輯結(jié)構(gòu)和線性表很相似,不同之處在于串針對的是字符集,也就是串中的元素都是字符。因此,對于串的基本操作和線性表的操作是有很大差別的。線性表更關注的是單個元素的操作,比如查找一個元素,插入或刪除一個元素,但串中更多的是查找子串的位置、得到指定位置的子串、替換子串等操作。

    ADT 串(string) Data串中元素僅由一個個字符組成,相鄰元素具有前驅(qū)和后繼關系。 OperationStrAssign(T,*chars):生成一個其值等于字符串常量chars的串T。StrCopy(T,S):串s存在,有串s復制得到串TClearString(S):串s存在,將串清空StringEmpty(S):若串S為空,返回true,否則返回falseStrLength(S):返回串S的元素個數(shù),即串的長度StrCompare(S,T):若S>T,返回值>0,若S=T,返回0;若S<T,返回值<0Concat(T,S1,S2):用T返回由S1和S2連接而成的新串SubString(Sub,S,pos,len):串S存在,1≤pos≤StrLength(S),0≤len≤StrLength(S)-pos+1,用Sub返回串S的第pos個字符起長度為len的子串Index(S,T,pos):串S和T存在,T是非空串,1≤pos≤StrLength(S),若主串S中存在和串T值相同的子串,則返回它在主串S中第pos個字符之后第一次出現(xiàn)的位置,否則返回0Replace(S,T,V):串S、T、V存在,T是非空串。用V替換主串S中出現(xiàn)的所有與T相等的不重疊的子串。StrInsert(S,pos,T):串S和T存在,1≤pos≤StrLength(S)+1.在串S的第pos個字符之前插入串TStrDelete(S,pos,len):串S存在,1≤pos≤StrLength(S)-len+1,從串S中刪除第pos個字符起長度為len的子串。 endADT

    對于不同的高級語言,對于串的基本操作會有不同的定義方法,所以在用某個語言操作字符串時,需要先查看它的參考手冊關于字符串的基本操作。不過還好,不同語言除方法名之外,操作實質(zhì)都是類似的。比如C#語言,字符串操作就還有ToLower轉(zhuǎn)小寫,ToUpper轉(zhuǎn)大寫,IndexOf從左查找子串位置,LastIndexOf從右查找子串位置,Trim去除兩邊空格等比較方便的操作,它們其實就是前面這些基本操作的擴展函數(shù)。

    我們來看一個操作Index 的實現(xiàn)算法。

    int Index(String S ,String T,int pos){int n,m,i;String sub;if(pos>0){n=StrLength(S);//得到主串S的長度m=StrLength(T);//得到子串T的長度i=pos;while(i<=n-m+1){SubString(sub,S,i,m);//取主串第i個位置上長度與T相等的子串給subif(StrCompare(sub,T)!=0) ++i;// 如果兩串不相等else return i; //如果兩串相等}}return 0; //如果子串與T相等,返回0 }

    其中用到了StrLength,SubString,StrCompare等基本操作來實現(xiàn)。

    5.5 串的存儲結(jié)構(gòu)

    串的存儲結(jié)構(gòu)和線性表相同,分為兩種。

    5.5.1 串的順序存儲結(jié)構(gòu)

    串的順序存儲結(jié)構(gòu)是用一組地址連續(xù)的存儲單元來存儲串中的字符序列的。按照預定義的大小,為每個定義的串變量分配一個固定長度的存儲區(qū)。一般用定長數(shù)組來定義。

    既然是定長數(shù)組,就存在一個預定義的最大串長度,一般可以將實際的串長度值保存在數(shù)組0下標位置,有的書中也會定義存儲在數(shù)組的最后一個下標位置。但是有些編程語言不想這么干,覺得存?zhèn)€數(shù)占空間麻煩。它規(guī)定在串值后面加一個不計入串長度的結(jié)束標記字符,比如“\0”來表示串值的終結(jié),這個時候,你想要知道此時的串長度,就需要遍歷計算一下才知道,其實這還是需要占用一個空間,何必呢。

    剛才講的串的順序存儲結(jié)構(gòu)其實是有問題的,因為字符串的操作,比如兩串的連接Concat,兩串的插入StrInsert,以及字符串的替換Replace,都有可能使得串序列的長度超過了數(shù)組的長度MaxSize。

    于是對于串的順序存儲,有一些變化,串值的存儲空間可在程序執(zhí)行過程中動態(tài)分配而得。比如在計算機中存在一個自由存儲區(qū),叫做“堆”。這個堆可由C語言的動態(tài)分配函數(shù)malloc()和free()來管理。

    5.5.2 串的鏈式存儲結(jié)構(gòu)

    對于串的鏈式存儲結(jié)構(gòu),與線性表是相似的,但由于串結(jié)構(gòu)的特殊性,結(jié)構(gòu)中的每個元素數(shù)據(jù)是一個字符,如果也簡單的應用鏈表存儲串值,一個結(jié)點對應一個字符,就會存在很大的空間浪費。因此,一個結(jié)點可以存放一個字符,也可以考慮存放多個字符,最后一個結(jié)點若是未被占滿時,可以用“#”或其他非串值字符補全,如圖所示

    當然,這里一個結(jié)點存多少個字符才合適就變得很重要,這會直接影響著串處理的效率,需要根據(jù)實際情況做出選擇。

    但串的鏈式存儲結(jié)構(gòu)除了在連接串與串操作時有一定方便之外,總的來說不如順序存儲靈活,性能也不如順序存儲結(jié)構(gòu)好。

    5.6樸素的模式匹配算法

    在計算機文獻中常用的英文單詞有哪些呢?想寫個程序,只要輸入一些英文的文檔,就可以計算出這當中所用頻率最高的詞匯是哪些。當然,實際操作時有很多困難,不過,這里面最重要其實就是去找一個單詞在一篇文章(相當于一個大字符串)中的定位問題。這種子串的定位操作通常稱作串的模式匹配,應該算是串中最重要的操作之一。

    假設我們要從下面的主串S="goodgoogle"中,找到T="google"這個子串的位置。我們通常需要下面的步驟。

    1 主串S第一位開始,S與T前三個字母都匹配成功,但S的第四個字母是d而T的字母是。第一位匹配失敗。如下圖所示,其中豎直連線表示相等,閃電狀彎折線表示不等。

    2 主串S第二位開始,主串S首字母是o,要匹配的T首字母是g,匹配失敗。如下圖所示

    3 主串S第三位開始,主串S首字母是o,要匹配的T首字母是g,匹配失敗,如下圖所示

    4 主串S第四位開始,主串S首字母是d,而T首字母是g,匹配失敗,如下圖

    5 主串S第五位開始,S與T,6個字母全匹配,匹配成功,如下圖所示

    簡單地說,就是對主串的每個字符作為子串的開頭,與要匹配的字符串進行匹配。對主串做大循環(huán),每個字符開頭做T的長度的小循環(huán),直到匹配成功或全部遍歷完成為止。

    前面我們已經(jīng)用串的其他操作實現(xiàn)了模式匹配算法Index。現(xiàn)在考慮不用串的其他操作,而是只用基本的數(shù)組來實現(xiàn)同樣的算法。注意我們假設主串S和要匹配的子串T的長度存在S[0]和T[0]中,實現(xiàn)的代碼如下

    //返回子串T在主串S中第pos個字符之后的位置。若不存在,則函數(shù)返回值為0int Index(String S,String T,int pos){int i=pos;int j=1;while(i<=S[0]&&j<=T[0]){if(S[i]==T[j]) ++i,++j;else{指針后退重新開始匹配i=i-j+2;//i回退到上次匹配首位的下一位j=1;//j回退到子串T的首位}}if(j>T[0]) return i-T[0];//T[0]是子串的長度else return 0;}

    分析一下,最好的情況是什么?那就是一開始就匹配成功,時間復雜度為O(1)。稍差一些,就像第二、三位一樣,每次都是首字母不匹配,那么對T串的循環(huán)就不必進行了,比如“abcdef”里面找"goo"。那么時間復雜度為O(n+m),其中n為主串長度,m為要匹配的字串長度。一般情況,根據(jù)等概率原則,平均是m+n2\frac{m+n}{2}2m+n?次查找,時間復雜度為O(m+n).

    那么最壞的情況呢? 就是每次不成功的匹配都發(fā)生在串T的最后一個字符。舉一個極端的例子。主串S為"00…000001",而要匹配的子串T為"0000000001",前者是有49個0和1個1的主串,后者是9個0和1個1的子串。在匹配時,每次都得將T中字符循環(huán)到最后一位才發(fā)現(xiàn):哦,原來它們不匹配啊。這樣等于T串需要在S串的前40個位置都需要判斷10次,并得出不匹配的結(jié)論,如下圖所示

    直到最后第41個位置,因為全部匹配成功,所以不需要再繼續(xù)下去,如下圖所示。如果最終沒有可匹配的子串,比如T是“00000000002”,到了第41個位置判斷不匹配后同樣不需要繼續(xù)比對下去。因此最壞情況的時間復雜度是O((n-m+1)*m)。

    不要以為這是很少見的情況,相反,在實際運用中,對于計算機來說,處理的都是二進制位的01串,一個字符的ASCII碼也可以看成是8位的二進制位01串,當然,漢字等所有的字符也都可以看成是多個01串。再比如像計算機圖形也可以理解成是由許許多多個0和1的串組成的。所以在計算機的運算當中,模式匹配操作可以說是隨處可見,而剛才的這個算法,就顯得太低效率。

    5.7 KMP模式匹配算法

    在很多年前,我們的科學家們,覺得像這種有多個0和1重復字符的字符串,卻需要挨個遍歷的算法是非常糟糕的事情。于是有三位前輩,D.E.Knuth,J.H.Morris 和V.R.Pratt 發(fā)表一個模式匹配算法,可以大大避免重復遍歷的情況,我們把它稱為克努特-莫里斯-普拉特算法,簡稱KMP算法。

    (未完待續(xù))

    5.7.1 KMP模式匹配算法原理

    5.7.2 next數(shù)組值推導

    5.7.3 KMP模式匹配算法實現(xiàn)

    5.7.4 KMP模式匹配算法改進

    5.7.5 nextval數(shù)組值推導

    總結(jié)

    以上是生活随笔為你收集整理的《大话数据结构》读书笔记-串的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。