資料結構(四)--字串操作
字串
1.串的定義
串是由零個或多個字元組成的有限序列,一般記為
串中任意個連續的字元組成的子序列稱為該串的子串。包含子串的串稱為主串。
串的原子操作(其餘操作可以以下操作組合完成)包含以下5種:
- 串賦值StrAssign
- 串比較StrCompare
- 求串長StrLength
- 串連線Concat
- 求子串SubString
2. 串的表示與實現
串有3種表示方式:
- 定長順序儲存(定長)
- 堆分配儲存(順序表)
- 塊串儲存(連結串列)
2.1 定長順序儲存
定長順序儲存用一組地址連續的儲存單元儲存字元序列。其定義如下
//----------------定長順序儲存------------
/**
* 定長串約定:
* 1.0號下表表示為串長
* 2.當串長長度超過MAX_SIZE時進行截斷
*/
#define MAX_SIZE 255
typedef unsigned char SString[MAX_SIZE + 1 + 1]; //0號下標儲存字串的長度,C語言特性字串最後一個尾'\0'
下面來分別實現上述5個原子操作:
2.1.1串賦值StrAssign
/**
* 字串分配,將str分配到T中
* @param T
* @param str
* @return
*/
Status strAssign(SString &T,char *str){
int length = 0;
char *p_c = str;
for(;*p_c!='\0';length++,p_c++); //求字串str的長度
if(length > MAX_SIZE){
T[0] = MAX_SIZE;
for(int i = 1;i<=MAX_SIZE;i++){ //超過maxsize的進行截斷
T[i] = str[i-1];
}
T[T[0 ] + 1] = '\0';
}else{
T[0] = length;
for(int i = 1;i<=length;i++){
T[i] = str[i-1];
}
T[T[0] + 1] = '\0';
}
return OK;
}
2.1.2 串比較StrCompare
/**
* 比較S和T
* @param S
* @param T
* @return S>T 返回>0
* S<T 返回<0
* S=T 返回0
*/
Status strCompare(SString &S, SString &T){
for(int i = 0;i<S[0] && i<T[0];i++){
if(S[i+1] != T[i+1]) return S[i+1] - T[i+1];
}
return S[0] - T[0]; //某一個串是另一串的子串時,比較哪個串更長
}
2.1.3 求串長
/**
* 求串長
* @param S
* @return
*/
int strLength(SString &S){
return S[0];
}
2.1.4 串連線Concat
/**
* 串連線,將S1和S2拼接成新串T。
* 由於定長串固有的缺點,分為以下三種情況:
* 1.S1,S2在拼接過程中均未截斷
* 2.S1,S2在拼接過程中S2被截斷
* 3.S1,S2在拼接過程中S1和S2均被截斷(S1部分截斷,S2全部截斷)
* @param T
* @param S1
* @param S2
* @return OK
*
*/
Status Concat(SString &T,SString S1,SString S2){
if(S1[0] + S2[0] <= MAX_SIZE){
T[0] = S1[0] + S2[0];
for(int i = 1;i<=S1[0];i++){
T[i] = S1[i];
}
for(int i = 1+S1[0];i<= S1[0]+S2[0];i++){
T[i] = S2[i - S1[0]];
}
T[T[0] + 1] = '\0';
}else if(S1[0] <= MAX_SIZE){
T[0] = MAX_SIZE;
for(int i = 1;i<=S1[0];i++){
T[i] = S1[i];
}
for(int i = 1+S1[0];i<=MAX_SIZE;i++){
T[i] = S2[i-S1[0]];
}
T[T[0] + 1] = '\0';
}else{
T[0] = MAX_SIZE;
for(int i =1;i<=MAX_SIZE;i++){
T[i] = S1[i];
}
T[T[0] + 1] = '\0';
}
return OK;
}
2.1.5 求子串SubString
/**
* 求S的一個子串,其位置在pos->pos+len-1
* @param Sub
* @Param S
* @param pos
* @param len
* @return
*/
Status SubString(SString &Sub,SString S,int pos,int len){
//判定位置
if(pos<1 || pos> S[0] || len <0 || pos+len -1 > S[0]) exit(ERROR);
Sub[0] = len;
for(int i = 1;i<=len;i++){
Sub[i] = S[i+pos-1];
}
Sub[Sub[0] + 1] = '\0';
return OK;
}
2.2 堆分配儲存表示
在順序儲存結構中,實現串操作的源操作為“字元序列的賦值”,操作的時間複雜度基於賦值的字元序列的長度。另一操作特點是,如果在操作中出現串序列超過上界,會出現截斷的情況,為客服這個缺點,可通過動態分配串值得儲存空間。基於堆分配儲存的定義如下:
//---------------堆分配字串------------
typedef struct{
char *ch;
int length;
}HString;
下面來實現5個原子操作:
2.2.1 串賦值StrAssign
/**
* 分配
* @param T
* @param chars
* @return
*/
Status StrAssign(HString &T,char *chars){
if(T.ch) free(T.ch); T.ch = NULL;
int length;
char *c = chars;
for(length = 0;*c!='\0';c++,length++); //求chars長度
if(!length){
T.ch = NULL;
T.length = 0;
}else{
T.ch = (char *)malloc(length*sizeof(char));
if(!T.ch) exit(ERROR);
for(int i = 0;i<=length;i++){ //取=號是把 '\0'也放進去
T.ch[i] = chars[i];
}
T.length = length;
}
return OK;
}
2.2.2 串比較StrCompare
/**
* 字串比較
* @param S
* @param T
* @return
*/
int strCompare(HString S,HString T){
for(int i = 0;i<S.length&&i<T.length;i++){
if(S.ch[i] != T.ch[i]) return S.ch[i] - T.ch[i];
}
return S.length - T.length;
}
2.2.3 求串長
/**
* S長度
* @param S
* @return
*/
int strLength(HString &S){
return S.length;
}
2.2.4 串連線Concat
/**
* 字串連線
* @param T
* @param S1
* @param S2
* @return
*/
Status Concat(HString &T,HString S1,HString &S2){
if(T.ch) free(T.ch); T.ch = NULL;
T.ch = (char *)malloc((S1.length+S2.length)*sizeof(char));
if(!T.ch) exit(ERROR);
for(int i = 0;i<S1.length;i++){
T.ch[i] = S1.ch[i];
}
for(int i = S1.length;i<=S1.length+S2.length;i++){
T.ch[i] = S2.ch[i-S1.length];
}
T.length = S1.length+S2.length;
return OK;
}
2.2.5 求子串SubString
Status SubString(HString &Sub,HString S,int pos,int len){
//位置判定
if(pos<1 || pos>S.length || len <0 || pos+len -1 > S.length) exit(ERROR);
if(Sub.ch) free(Sub.ch); Sub.ch = NULL;
if(!len){
Sub.ch = NULL;
Sub.length = 0;
}else{
Sub.ch = (char *)malloc(len*sizeof(char));
for(int i = 0;i<len;i++){
Sub.ch[i] = S.ch[i+ pos-1];
}
Sub.length = len;
Sub.ch[Sub.length -1] = '\0';
}
return OK;
}
2.3 塊鏈
塊鏈的操作不如上述兩種結構方便,且佔用儲存量大,這裡不做討論。
3. KMP演算法
子串的定位操作通常稱做串的模式匹配,可以採用暴力尋找,不過這樣的時間複雜度過高,最為經典的演算法是KMP演算法。網上關於該演算法的文章有很多很多,不過目前我所看到最通俗易懂的是這篇,有需要的朋友可以去看看。
4.總結
字串的處理作為一種非常重要的處理方式被廣泛使用,雖然現在最多高階語言都封裝了String類(如Java,C++等),不過作為一種知識的補充也算是非常不錯的。嗯,就這樣吧。
相關推薦
資料結構(四)--字串操作
字串 1.串的定義 串是由零個或多個字元組成的有限序列,一般記為 s=′a1a2…a′ns=′a1a2…an′ 串中任意個連續的字元組成的子序列稱為該串的子串。包含子串的串稱為主串。 串的原子操作(其餘操作可以以下操作組合完成)包含以下5種:
大話資料結構(四)——雙向連結串列的java實現
在實現了單向連結串列後,我們在使用單向連結串列中會發現一個問題:在單向連結串列中查詢某一個結點的下一個結點的時間複雜度是O(1),但是查詢這個結點的上一個結點的時候,時間複雜度的最大值就變成了O(n),因為在查詢這個指定結點的上一個結點時又需要從頭開始遍歷。 那麼該如何解決這個困難呢?
資料結構(四)佇列
一、基本概念 1、特點: 在佇列頭部進行刪除,在佇列的尾部進行插入操作 2、主要實現: 使用迴圈陣列 使用連結串列 3、關係圖: 二、Queue public interface Queue<E> extends
基礎演算法與資料結構(四)最短路徑——Dijkstra演算法
一般最短路徑演算法習慣性的分為兩種:單源最短路徑演算法和全頂點之間最短路徑。前者是計算出從一個點出發,到達所有其餘可到達頂點的距離。後者是計算出圖中所有點之間的路徑距離。 單源最短路徑 Dijkstra演算法 思維 本質上是貪心的思想,宣告一個數組dis來儲存源點到各個頂點的最短距離和一個儲存已經
資料結構(四)之二叉樹
二叉樹 二叉樹可以用陣列和鏈式結構這兩種方式來建立,這裡只介紹二叉樹的鏈式結構,並且實現二叉樹的前序、中序和後序遍歷。(運用二叉樹組定義靜態二叉樹的方式以註釋的形式寫明) 二叉樹的建立有三種方式:前序、中序和後序。這裡只展現了前序遍歷的方式。 #include<
再談資料結構(四):排序與查詢
1 - 引言 雖然C++中的STL庫中提供了許多排序和查詢的方法。但是我們還是需要了解一下排序和查詢內部的原理,下面讓我們學習一下各類排序與查詢演算法 2 - 歸併排序 第一種高效的排序演算法是歸併排序,按照分治三步法,對歸併排序演算法介紹如下: 劃分問題:把序列分成
資料結構(四)二叉樹的遍歷
二叉樹的遍歷 0. 樹的表示 typedef struct TreeNode *BinTree; struct TreeNode{ int Data; // 存值 BinTree Left; // 左兒子結點 BinTree Right;
自己動手實現java資料結構(四)雙端佇列
自己動手實現java資料結構(四)雙端佇列 1.雙端佇列介紹 在介紹雙端佇列之前,我們需要先介紹佇列的概念。和棧相對應,在許多演算法設計中,需要一種"先進先出(First Input First Output)"的資料結構,因而一種被稱為"佇列(Queue)"的資料結構被抽象了出來(因為
資料結構(四)C++動態儲存分配
1.運算子new 要為一個整數動態分配儲存空間,可以用下面的語句說明一個整型指標變數int *x;當需要使用該整型時,可用下面的語句為它分配儲存空間: y=new int; 為了在剛分配的空間中儲存一個整數值10, *y=10; int
資料結構(四)之非遞迴遍歷二叉樹
void Inoder(Bitree root)//二叉樹的中序遍歷非遞迴 { IniStack(&S);//初始化一個棧 p=root; while(!isEmpty(S)||p!=NULL) { if(p!=NULL)//如果當前結點不為空進棧 { pu
資料結構(四)——線性結構之佇列Queue
1.佇列 佇列是先進先出的線性表。只允許在表的一端進行插入操作,而在另一端進行刪除操作。 進行插入的一端稱為隊尾,進行刪除操作的一端稱為隊頭。 在具體應用中通常用連結串列或者陣列來實現。 2.對佇列的操作 佇列我們可以想像成一個數組,每次插入插入在陣列的最後一位,取從第一位
資料結構(四)python使用順序表實現棧
概念: 棧(stack),有些地方稱為堆疊,是一種容器,可存入資料元素、訪問元素、刪除元素,它的特點在於只能允許在容器的一端(稱為棧頂端指標,英語:top)進行加入資料(英語:push)和輸出資料(英語:pop)的運算。沒有了位置概念,保證任何時候可以訪問、刪除的元素都是此前最後存入的那個元
挖掘演算法中的資料結構(四):堆排序之 二叉堆(Heapify、原地堆排序優化)
不同於前面幾篇O(n^2)或O(n*logn)排序演算法,此篇文章將講解另一個排序演算法——堆排序,也是此係列的第一個資料結構—–堆,需要注意的是在堆結構中排序是次要的,重要的是堆結構及衍生出來的資料結構問題,排序只是堆應用之一。 此篇涉及的知識點有: 堆
資料結構線性表的邏輯結構(四)單鏈表的基本操作的實現
一、 實驗目的1. 掌握線性表的邏輯結構;2. 連結串列的基本操作的實現;3. 掌握利用C++/C程式語言實現資料結構的程式設計方法;4. 通過上機時間加強利用資料結構解決實際應用問題的能力;二、 實驗要求1. 實驗前做好充分準備,包括複習線性表所學內容,事先預習好本次實驗內
資料結構(一):順序表的基本操作 C語言
順序表 標頭檔案: Sqlist.h #include<stdio.h> #include<stdlib.h> #define SIZE 15 #pragma once typedef struct Sqlist { int elem[SIZ
java資料結構(一)----------順序表操作例項
import java.util.Scanner; class DATA{//資料類 String key; // 節點的關鍵字 String name; String age; } class SLType{// 定義順序表的結構陣列 static fina
SIMD資料並行(四)——三種結構的比較
在計算機體系中,資料並行有兩種實現路徑:MIMD(Multiple Instruction Multiple Data,多指令流多資料流)和SIMD(Single Instruction Multiple Data,單指令流多資料流)。其中MIMD的表現形式主要有多發射、多執行緒、多核心,在當代設計的以處
資料結構(七)——樹結構(Tree) 之二叉樹的常用操作
一.鏈式儲存的二叉樹的操作 1.遍歷二叉樹 先序遍歷:根-->左-->右 中序遍歷:左-->根-->右 後序遍歷:左-->右-->根 2.二叉樹結點的查詢 結點的查詢也可以分為先序查詢,中序查詢和後序查詢。方式和遍
資料結構(二)---基本的棧的操作
棧的基本操作 在這周自己學了一下簡單的棧的基本操作,對棧有了個基本的瞭解,就比如說,在程式裡記憶體分為靜態記憶體和動態記憶體,這兩者的區別是,靜態記憶體是放在棧裡的,由系統分配記憶體的;動態記憶體是存放在堆裡,由程式設計師手動給的。 棧的本質來講這是一種儲存
小白學 Python 資料分析(5):Pandas (四)基礎操作(1)檢視資料
在家為國家做貢獻太無聊,不如跟我一起學點 Python 人生苦短,我用 Python 前文傳送門: 小白學 Python 資料分析(1):資料分析基礎 小白學 Python 資料分析(2):Pandas (一)概述 小白學 Python 資料分析(3):Pandas (二)資料結構 Series