從存儲和數據判斷串的特殊性
❶ 長度為n的鏈隊列用單循環鏈表表示,若只設頭指針,則怎樣進行入隊和出隊操作;若只設尾指針呢
就行了,准過!
第一章:緒論
1.1:數據結構課程的任務是:討論數據的各種邏輯結構、在計算機中的存儲結構以及各種操作的演算法設計。
1.2:數據:是客觀描述事物的數字、字元以及所有的能輸入到計算機中並能被計算機接收的各種集合的統稱。
數據元素:表示一個事物的一組數據稱作是一個數據元素,是數據的基本單位。
數據項:是數據元素中有獨立含義的、不可分割的最小標識單位。
數據結構概念包含三個方面:數據的邏輯結構、數據的存儲結構的數據的操作。
1.3數據的邏輯結構指數據元素之間的邏輯關系,用一個數據元素的集合定義在此集合上的若干關系來表示,數據結構可以分為三種:線性結構、樹結構和圖。
1.4:數據元素及其關系在計算機中的存儲表示稱為數據的存儲結構,也稱為物理結構。
數據的存儲結構基本形式有兩種:順序存儲結構和鏈式存儲結構。
2.1:演算法:一個演算法是一個有窮規則的集合,其規則確定一個解決某一特定類型問題的操作序列。演算法規則需滿足以下五個特性:
輸入——演算法有零個或多個輸入數據。
輸出——演算法有一個或多個輸出數據,與輸入數據有某種特定關系。
有窮性——演算法必須在執行又窮步之後結束。
確定性——演算法的每個步驟必須含義明確,無二義性。
可行性——演算法的每步操作必須是基本的,它們的原則上都能夠精確地進行,用筆和紙做有窮次就可以完成。
有窮性和可行性是演算法最重要的兩個特徵。
2.2:演算法與數據結構:演算法建立數據結構之上,對數據結構的操作需用演算法來描述。
演算法設計依賴數據的邏輯結構,演算法實現依賴數據結構的存儲結構。
2.3:演算法的設計應滿足五個目標:
正確性:演算法應確切的滿足應用問題的需求,這是演算法設計的基本目標。
健壯性:即使輸入數據不合適,演算法也能做出適當的處理,不會導致不可控結
高時間效率:演算法的執行時間越短,時間效率越高。 果。
高空間效率:演算法執行時佔用的存儲空間越少,空間效率越高。
可讀性:演算法的可讀性有利於人們對演算法的理解。
2.4:度量演算法的時間效率,時間復雜度,(課本39頁)。
2.5:遞歸定義:即用一個概念本身直接或間接地定義它自己。遞歸定義有兩個條件:
至少有一條初始定義是非遞歸的,如1!=1.
由已知函數值逐步遞推計算出未知函數值,如用(n-1)!定義n!。
第二章:線性表
1.1線性表:線性表是由n(n>=0)個類型相同的數據元素a0,a1,a2,…an-1,組成的有限序列,記作: LinearList = (a0,a1,a2,…an-1)
其中,元素ai可以是整數、浮點數、字元、也可以是對象。n是線性表的元素個數,成為線性表長度。若n=0,則LinearList為空表。若n>0,則a0沒有前驅元素,an-1沒有後繼元素,ai(0<i<n-1)有且僅有一個直接前驅元素ai-1和一個直接後繼元素ai+1。
1.2線性表的順序存儲是用一組連續的內存單元依次存放線性表的數據元素,元素在內存的物理存儲次序與它們在線性表中的邏輯次序相同。
線性表的數據元素數據同一種數據類型,設每個元素佔用c位元組,a0的存儲地址為
Loc(a0),則ai的存儲地址Loc(ai)為:Loc(ai) = Loc(a0)+ i*c
數組是順序存儲的隨機存儲結構,它佔用一組連續的存儲單元,通過下標識別元素,元素地址是下標的線性函數。
1.3:順序表的插入和刪除操作要移動數據元素。平均移動次數是 屬數據表長度的一半。(課本第50頁)
1.4:線性表的鏈式存儲是用若乾地址分散的存儲單元存儲數據元素,邏輯上相鄰的數據元素在物理位置上不一定相鄰,必須採用附加信息表示數據元素之間的順序關系。
它有兩個域組成:數據域和地址域。通常成為節點。(課本第55頁及56頁)
1.5單鏈表(課本56頁)
單鏈表的遍歷:Node<E> p = head; while(p!=null)
單鏈表的插入和刪除操作非常簡便,只要改變節點間的鏈接關系,不需移動數據元素。
單鏈表的插入操作:1):空表插入/頭插入 2)中間插入/尾插入
if(head == null) Node<E> q = new Node<E>(x);
{ head = new Node<E>(x); q.next = p.next;
}else{ p.next = q;
Node<E> q=new Node<E>(x); 中間插入或尾插入都不會改變單表
q.next = head; 的頭指針head。
head = q;
}
單鏈表的刪除操作:
頭刪除:head = head.next;
中間/尾刪除:if(p.next!=null)
循環單鏈表:如果單鏈表最後一個節點的next鏈保存單鏈表的頭指針head值,則該單鏈表成為環形結構,稱為循環單鏈表。(課本67)
若rear是單鏈表的尾指針,則執行(rear.next=head;)語句,使單鏈表成為一條循環單鏈表。當head.next==head時,循環單鏈表為空。
1.6:雙鏈表結構:雙鏈表的每個結點有兩個鏈域,分別指向它的前驅和後繼結點,
當head.next==null時,雙鏈表為空。
設p指向雙鏈表中非兩端的某個結點,則成立下列關系:p=p.next.prev=p.prev.next。
雙鏈表的插入和刪除:1)插入 2)刪除
q=new DLinkNode(x); p.prev.next = p.next;
q.prev=p.prev;q.next =p; if(p.next=null){
p.prev.next = q;p.prev=q; (p.next).prev = p.prev;}
循環雙鏈表:當head.next==head且head.prev==head時,循環雙鏈表為空。
第三章:棧和隊列
1.1棧:棧是一種特殊的線性表,其中插入和刪除操作只允許在線性表的一端進行。允許操作的一端稱為棧頂,不允許操作的一端稱為棧底。棧有順序棧和鏈式棧。
棧中插入元素的操作稱為入棧,刪除元素的操作稱為出棧。沒有元素的中稱為空棧。
棧的進出棧順序:後進先出,先進後出。(及75頁的思考題)。
1.2:隊列:隊列是一種特殊的線性表,其中插入和刪除操作分別在線性表的兩端進行。
向隊列中插入元素的過程稱為入隊,刪除元素的過程稱為出對,允許入隊的一端稱為隊尾,允許出隊的一端稱為對頭。沒有元素的隊列稱為空隊列。隊列是先進先出。
第四章:串
1.1:串是一種特殊的線性表,其特殊性在於線性表中的每個元素是一個字元。一個串記為: s=「s0s1s2…sn-1」 其中n>=0,s是串名,一對雙引號括起來的字元序列s0s1s2…sn-1是串值,si(i=0,1,2,…n-1)為特定字元集合中的一個字元。一個串中包含的字元個數稱為串的長度。
長度為0的串稱為空串,記作「」,而由一個或多個空格字元構成的字元串稱為空格串。
子串:由串s中任意連續字元組成的一個子序列sub稱為s的子串,s稱為sub的主串。子串的序號是指該子串的第一個字元在主串中的序號。
串比較:兩個串可比較是否相等,也可比較大小。兩個串(子串)相等的充要條件是兩個串(子串)的長度相同,並且各對應位置上的字元也相同。
兩個串的大小由對應位置的第一個不同字元的大小決定,字元比較次序是從頭開始依次向後。當兩個串長度不等而對應位置的字元都相同時,較長的串定義為較「大」。
第五章:數組和廣義表
1.1:數組是一種數據結構,數據元素具有相同的數據類型。一維數組的邏輯結構是線性表,多維數組是線性表的擴展。
1.2:一維數組:一維數組採用順序存儲結構。一個一維數組佔用一組連續的存儲單元。
設數組第一個元素a0的存儲地址為Loc(a0),每個元素佔用c位元組,則數組其他元素ai的存儲地址Loc(ai)為: Loc(ai)= Loc(a0)+i*c
數組通過下標識別元素,元素地址是下標的線性函數。一個下標能夠唯一確定一個元素,所劃給的時間是O(1)。因此數組是隨機存取結構,這是數組最大的優點。
1.3:多維數組的遍歷:有兩種次序:行主序和列主序。
行主序:以行為主序,按行遞增訪問數組元素,訪問完第i行的所有元素之後再訪問第i+1行的元素,同一行上按列遞增訪問數組元素。
a00,a01,…a0(n-1), a10,a11,…a1(n-1),…a(m-1)0,a(m-1)1,…,a(m-1)(n-1)
2)列主序:以列為主序,按列遞增訪問數組元素,訪問完第j列的所有元素之後再訪問第j+1列的元素,同一列上按列遞增訪問數組元素。
多維數組的存儲結構:多維數組也是由多個一維數組組合而成,組合方式有一下兩種。
靜態多維數組的順序存儲結構:可按行主序和列主序進行順序存儲。
按行主序存儲時,元素aij的地址為:Loc(aij)= Loc(a00)+(i*n+j)*c
按列主序存儲時,Loc(aij)= Loc(a00)+(j*m+i)*c
動態多維數組的存儲結構。
二維數組元素地址就是兩個下標的線性函數。無論採用哪種存儲結構,多維數組都是基於一維數組的,因此也只能進行賦值、取值兩種存取操作,不能進行插入,刪除操作。
第六章:
樹是數據元素(結點)之間具有層次關系的非線性結構。在樹結構中,除根以外的結點只有一個直接前驅結點,可以有零至多個直接後繼結點。根沒有前驅結點。
樹是由n(n>=0)個結點組成的有限集合(樹中元素通常稱為結點)。N=0的樹稱為空樹;n>0大的樹T;
@有一個特殊的結點稱為根結點,它只有後繼結點,沒有前驅結點。
@除根結點之外的其他結點分為m(m>=0)個互不相交的集合T0,T1,T3……..,Tm-1,其中每個集合Ti(0<=i<m)本身又是一棵樹,稱為根的子樹。
樹是遞歸定義的。結點是樹大的基本單位,若干個結點組成一棵子樹,若干棵互不相交的子樹組成一棵樹。樹的每個結點都是該樹中某一棵子樹的根。因此,樹是由結點組成的、結點之間具有層次關系大的非線性結構。
結點的前驅結點稱為其父母結點,反之,結點大的後繼結點稱為其孩子結點。一棵樹中,只有根結點沒有父母結點,其他結點有且僅有一個父母結點。
擁有同一個父母結點的多個結點之間稱為兄弟結點。結點的祖先是指從根結點到其父母結點所經過大的所有結點。結點的後代是指該結點的所有孩子結點,以及孩子的孩子等。
結點的度是結點所擁有子樹的棵數。度為0的結點稱為葉子結點,又叫終端結點;樹中除葉子結點之外的其他結點稱為分支結點,又叫非葉子結點或非終端結點。樹的度是指樹中各結點度的最大值。
結點的層次屬性反應結點處於樹中的層次位置。約定根結點的層次為1,其他結點的層次是其父母結點的層次加1。顯然,兄弟結點的層次相同。
樹的高度或深度是樹中結點的最大層次樹。
設樹中x結點是y結點的父母結點,有序對(x,y)稱為連接這兩個結點的分支,也稱為邊。
設(X0,X1,….,Xk-1)是由樹中結點組成的一個序列,且(Xi,Xi+1)(0<=i<k-1)都是樹中的邊,則該序列稱為從X0到Xk-1的一條路徑。路徑長度為路徑上的邊數。
在樹的定義中,結點的子樹T0,T1…..,Tm-1之間沒有次序,可以交換位置,稱為無序樹,簡稱樹。如果結點的子樹T0,T1……,Tm-1從左到右是有次序的,不能交換位置,則 稱該樹為有序樹。
森林是m(m>=0)棵互不相乾的樹的集合。給森林加上一個根結點就變成一棵樹,將樹的根節點刪除就變成森林。
二叉樹的性質1:若根結點的層次為1,則二叉樹第i層最多有2 的i-1次方(i>=1)個結點。
二叉樹的性質2:在高度為k的二叉樹中,最多有2的k次方減一個結點。
二叉樹的性質3:設一棵二叉樹的葉子結點數為n0,2度結點數為n2,則n0=n2+1。
一棵高度為k的滿二叉樹是具有2的k次方減一個結點的二叉樹。滿二叉樹中每一層的結點數目都達到最大值。對滿二叉樹的結點進行連續編號,約定根節點的序號為0,從根節點開始,自上而下,每層自左至右編號。
一棵具有n個結點高度為k的二叉樹,如果他的每個節點都與高度為k的滿二叉樹中序號為0~n-1
的結點一一對應,則這棵二叉樹為為完全二叉樹。
滿二叉樹是完全二叉樹,而完全二叉樹不一定是滿二叉樹。完全二叉樹的第1~k-1層是滿二叉樹第k層不滿,並且該層所有結點必須集中在該層左邊的若干位置上。
二叉樹的性質4:一棵具有n個結點的完全二叉樹,其高度k=log2n的絕對值+1
二叉樹的性質5:一棵具有n個結點的完全二叉樹,對序號為i的結點,有
@若i=0,則i為根節點,無父母結點;若i>0,則i的父母結點的序號為[(i-1)/2]。
@若2i+1<n,則i的左孩子結點序號為2i+1;否則i無左孩子。
@若2i+2<n,則i的右孩子結點的序號為2i+2,否則i無右孩子。
二叉樹的遍歷
二叉樹的遍歷是按照一定規則和次序訪問二叉樹中的所有結點,並且每個結點僅被訪問一次。
二叉樹的三種次序遍歷
1:先根次序;訪問根節點,遍歷左子樹,遍歷右子樹。
2:中根次序;遍歷左子樹,訪問右子樹,遍歷右子樹。
3:後根次序;遍歷左子樹,遍歷右子樹,訪問根節點。
先根次序遍歷時,最先訪問根節點;後根次序遍歷時,最後訪問根節點;中根次序遍歷時,左子樹上的結點在根節點之前訪問,右子樹上的結點在根節點之後訪問。
二叉樹的插入和刪除操作P147
二叉樹的層次遍歷P149
習題P167 6—10,6—19
第七章
圖是由定點集合及頂點間的關系集合組成的一種數據關邊系。頂點之間的關系成為邊。一個圖G記為G=(V,E),V是頂點A的有限集合,E是邊的有限集合。即 V=
E=或E=其中Path(A,B)表示從頂點A到B的一條單向通路,即Path(A,B)是有方向的。
無向圖中的邊事沒有方向,每條邊用兩個頂點的無序對表示。
有向圖中的邊是有方向,每條邊用兩個頂點的有序對表示。
完全圖指圖的邊數達到最大值。n個頂點的完全圖記為Kn。無向完全圖Kn的邊數為n*(n-1)/2,有向完全圖Kn的邊數為n*(n-1)。
子圖:設圖G==(V,E),G』=(V』,E』),若V』包含於V且E』包含於E,則稱圖G』是G的子圖。若G』是G的真子圖。
連通圖:在無向圖G中,若從頂點VI到Vj有路徑,則稱Vi和Vj是聯通的。若圖G中任意一對頂點Vi和Vj(Vi不等於Vj)都是聯通的,則稱G為連通圖。非連通圖的極大聯通子圖稱為該圖的聯通分量。
強連通圖:在有向圖中,若在每一對頂點Vi和Vj(Vi不等於Vj)之間都存在一條從Vi到Vj的路徑,也存在一條從Vi到Vj的路徑,也存在一條從Vi到Vj的路徑,則稱該圖的強連通圖。非強連通圖的極大強連通子圖稱為該圖的強連通圖分量。
圖的遍歷
遍歷圖是指從圖G中任意一個頂點V出發,沿著圖中的邊前行,到達並訪問圖中的所有頂點,且每個頂點僅被訪問一次。遍歷圖要考慮一下三個問題:
@指定遍歷的第一個訪問頂點
@由於一個頂點可能與多個頂點相鄰,因此要在多個鄰接頂點之間約定一種訪問次序。
@由於圖中可能存在迴路,在訪問某個頂點之後,可能沿著某條路徑又回到該頂點。
深度優先搜索
圖的深度優先搜索策略是,訪問某個頂點v,接著尋找v的另一個未被訪問的鄰接頂點w訪問,如此反復執行,走過一條較長路徑到達最遠頂點;若頂點v沒有未被訪問的其他鄰接頂點,則回到前一個被訪問頂點,再尋找其他訪問路徑。
圖的深度優先搜索遍歷演算法P188
聯通的無迴路的無向圖,簡稱樹。樹中的懸掛點又成為樹葉,其他頂點稱為分支點。各連通分量均為樹的圖稱為森林,樹是森林。
由於樹中無迴路,因此樹中必定無自身環也無重邊(否則他有迴路)若去掉樹中的任意一條邊,則變成森林,成為非聯通圖;若給樹加上一條邊,形成圖中的一條迴路,則不是樹。P191
生成樹和生成森林:
一個連通無向圖的生成樹是該圖的一個極小聯通生成子圖,它包含原圖中所有頂點(n個)以及足以構成一棵樹的n-1條邊。
一個非聯通的無向圖,其各連通圖分量的生成圖組成該圖的生成森林。
圖的生成圖或生成森林不是唯一的,從不同頂點開始、採用不同遍歷可以得到不同的生成樹或森林。
在生成樹中,任何樹中,任何兩個頂點之間只有唯一的一條路徑。
第八章
折半查找演算法描述 P206,P207
二叉排序樹及其查找:
二叉排序樹或者是一棵空樹;或者是具有下列性質的二叉樹:
@每個結點都有一個作為查找依據的關鍵字,所有結點的關鍵字互不相同。
@若一個結點的左子樹不空,則左子樹上所有結點的關鍵字均小於這個節點的關鍵字;
@每個結點的左右子樹也分別為二叉排序樹。
在一棵二叉排序樹中,查找值為value的結點,演算法描述如下:
@從根結點開始,設p指向根結點
@將value與p結點的關鍵字進行比較,若兩者相等,則查找成功;若value值較小,則在p的左子樹中繼續查找;若value值較大,則在p的右子樹中繼續查找。
@重復執行上一步,直到查找成功或p為空,若p為空,則查找不成功。
習題 8-6
第九章
直接插入排序演算法描述:p228
冒泡排序演算法的描述:p232
快速排序演算法描述p233
直接選擇排序演算法描述p236
直接選擇排序演算法實現如下:
Public static void selectSort(int[]table){
for(int i=0;i<table.length-1;i++){
int min=I;
for(int j=i+1;j<table.length;j++){
if(table[j]<table[min])
min=j;
if(min!=i){
int temp=table[i];
table[i]==table[min];
table[min]=temp;
}
}
}
}
堆排序是完全二叉樹的應用,是充分利用完全二叉樹特性的一種選擇排序。
堆定義:設n個元素的數據序列,當且僅當滿足下列關系
k1<=k2i+1且ki<=k2i+2 i=0,1,2,3,….,[n/2-1]
或ki>==k2i+1且ki>=2i+2i=0,1,2,3,…..[n/2-1]時,序列稱為最小堆或最大堆。將最小(大)堆看成是一顆完全二叉樹的層次遍歷序列,則任意一個結點的關鍵字都小於等於(大於等於)它的孩子節點的關鍵字值,由此可知,根結點值最小(大)。根據二叉樹的性質5,完全二叉樹中的第i(0<=i<n)個結點,如果有孩子,則左孩子為第2i+1個結點,右孩子為第2i+2個結點。
希望對你會有所幫助。
❷ 408數據結構可以寫漢字嗎
408數據結構(全)
一個數據元素可由若干數據項組成,數據項是構成數據元素的不可分割的最小單位。所以可以寫漢字!!!
❸ 對於字元串computer七長度為四的字串共有幾個
4.1 串的定義和實現
穿的定義是有零個或者多個字元組成的有限序列,串中字元的個數稱為串的長度,n=0的時候串稱為空串。串中任意連續的字元組成的子序列稱為該串的子串。
串和線性表在邏輯結構上十分相似,但是基本操作上有很大區別,線性表的基本操作主要是以單個元素作為操作對象,但是串的基本操作通常以子串作為操作對象。
串的存儲結構
1.定長順序存儲表示
類似於線性表的順序存儲結構
#define MAXSIZE 255
typedef struct{
char ch[MAXSIZE];
int length;}
超過預定一長度的串值會被捨去,稱之為截斷
2.堆分配存儲表示
堆分配存儲仍然以一組地址連續的存儲單元存放串值的字元序列,但是他們的存儲空間是在執行過程中動態分配得到的。
3.塊鏈存儲表示
類似於線性表的鏈式存儲結構,也可以採用鏈表方式存儲串值。由於串的特殊性,在具體實現的時候,可以在每個節點存放一個字元或者多個字元。
TIPS:在字元串中,可以將char[0]設定為存儲長度的元素,但是最多隻能表示255;也可以使用\0表示字元串結尾,但是想要獲取長度需要遍歷整個串
4.2 串的模式匹配
簡單的模式匹配演算法
子串的定位操作稱為串的模式匹配,其中簡單的模式匹配演算法是一種不依賴其他串操作的暴力匹配演算法。其演算法思想是,將主串中和模式串等長的子串全部提取出來,並且依次對比。
暴力模式匹配演算法的最壞時間復雜度為O(nm),最好的時間復雜度為O(m),其中n,m分別為主串和模式串的長度。
改進的模式匹配演算法——KMP演算法
在暴力匹配演算法中,每次匹配失敗都是後移一位再從頭開始比較,但是比如:
在 a b a b c a b c a c 中查找abcac
這會導致一定的重復比較,從而導致效率下降(展開說)
1.字元串的前綴、後綴和部分匹配值
前綴指的是出最後一個字元外所有的頭部子串,後綴指的是除第一個字元外字元串的所有尾部子串;部分匹配值為字元串的前綴和後綴的最長相等前後綴長度。
在對比到第k個字元時,如果發生了串不匹配,可以尋找已匹配的串的最大公共前後綴,從而使得不需要重復對比。
在一個有n個字元的串中,可能存在n種匹配失敗的情景,對應的是n種部分匹配串。每一種部分匹配串的最大前後綴是固定的,因此可以提前計算出對比到k個字元錯配時主串需要前進的步數,並且將其存儲在next數組中。這樣在KMP演算法執行時,可以直接使用
重點:next數組的計算
最長相等前後綴長度可以使得主串不需要回退,故KMP演算法可以在O(n+m)的時間數量級上完成串的模式匹配操作,提高了模式匹配效率。其中,O(m)的時間復雜度是在求next數組時產生的,O(n)的時間復雜度是在執行KMP演算法時產生的。
總的來說,相對於樸素模式匹配演算法,KMP演算法能夠避免主串指針頻繁回溯,從而提高了效率
2.KMP演算法的原理是什麼
當子串與掃描到的主串不匹配的時候,首先計算出已匹配的子串的前綴和後綴的最大公共子集。然後可以將子串向後移動,將共有前綴移動到原子集的共有後綴處,從而避免重復查找,使得子串不需要回退。
右移位數 = 已匹配的字元數 - 對應的部分字元值
3.KMP演算法的進一步優化
KMP演算法在對比諸如"aaaab"這類串的時候,還是會出現重復匹配的問題,為了解決而需要在next數組的基礎上再進一步處理得到nextval數組
❹ 數據結構中關於串的一個簡單問題
在這里定義的兩個串:S1和T 如果可以執行T[1..S1[0]] = S1[1..S1[0]];
這條語句,那麼就表示該串是順序存儲的串,就想數組一樣的順序存儲方式,並且在該順序結構的第一個存儲單元中,存儲的是該存儲串的存儲單元的大小,所以該語句應該是:T[1..T[0]] = S1[1..S1[0]]; 表示的是,將S1中的所有內容按照下表數的對應放入到T串中,並且,兩個順序串的存儲空間大小最好一樣!呵呵。。。。。。在定義該串的結構的時候進行設定就行了!
❺ 比較字元串的問題字元串到底是怎麼儲存的
耐心的把下面的看完你就明白了:
簡單的說:
java把內存劃分成兩種:一種是棧內存,一種是堆內存。
在函數中定義的一些基本類型的變數和對象的引用變數都在函數的棧內存中分配。
當在一段代碼塊定義一個變數時,Java就在棧中為這個變數分配內存空間,當超過變數的作用域後,Java會自動釋放掉為該變數所分配的內存空間,該內存空間可以立即被另作他用。
堆內存用來存放由new創建的對象和數組。
在堆中分配的內存,由Java虛擬機的自動垃圾回收器來管理。
在堆中產生了一個數組或對象後,還可以在棧中定義一個特殊的變數,讓棧中這個變數的取值等於數組或對象在堆內存中的首地址,棧中的這個變數就成了數組或對象的引用變數。
引用變數就相當於是為數組或對象起的一個名稱,以後就可以在程序中使用棧中的引用變數來訪問堆中的數組或對象。
具體的說:
棧與堆都是Java用來在Ram中存放數據的地方。與C++不同,Java自動管理棧和堆,程序員不能直接地設置棧或堆。
Java的堆是一個運行時數據區,類的(對象從中分配空間。這些對象通過new、newarray、anewarray和multianewarray等指令建立,它們不需要程序代碼來顯式的釋放。堆是由垃圾回收來負責的,堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,因為它是在運行時動態分配內存的,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由於要在運行時動態分配內存,存取速度較慢。
棧的優勢是,存取速度比堆要快,僅次於寄存器,棧數據可以共享。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本類型的變數(,int, short, long, byte, float, double, boolean, char)和對象句柄。
棧有一個很重要的特殊性,就是存在棧中的數據可以共享。假設我們同時定義:
int a = 3;
int b = 3;
編譯器先處理int a = 3;首先它會在棧中創建一個變數為a的引用,然後查找棧中是否有3這個值,如果沒找到,就將3存放進來,然後將a指向3。接著處理int b = 3;在創建完b的引用變數後,因為在棧中已經有3這個值,便將b直接指向3。這樣,就出現了a與b同時均指向3的情況。這時,如果再令a=4;那麼編譯器會重新搜索棧中是否有4值,如果沒有,則將4存放進來,並令a指向4;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。要注意這種數據的共享與兩個對象的引用同時指向一個對象的這種共享是不同的,因為這種情況a的修改並不會影響到b, 它是由編譯器完成的,它有利於節省空間。而一個對象引用變數修改了這個對象的內部狀態,會影響到另一個對象引用變數。
String是一個特殊的包裝類數據。可以用:
String str = new String("abc");
String str = "abc";
兩種的形式來創建,第一種是用new()來新建對象的,它會在存放於堆中。每調用一次就會創建一個新的對象。
而第二種是先在棧中創建一個對String類的對象引用變數str,然後查找棧中有沒有存放"abc",如果沒有,則將"abc"存放進棧,並令str指向」abc」,如果已經有」abc」 則直接令str指向「abc」。
比較類裡面的數值是否相等時,用equals()方法;當測試兩個包裝類的引用是否指向同一個對象時,用==,下面用例子說明上面的理論。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和str2是指向同一個對象的。
String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用new的方式是生成不同的對象。每一次生成一個。
因此用第一種方式創建多個」abc」字元串,在內存中其實只存在一個對象而已. 這種寫法有利與節省內存空間. 同時它可以在一定程度上提高程序的運行速度,因為JVM會自動根據棧中數據的實際情況來決定是否有必要創建新對象。而對於String str = new String("abc");的代碼,則一概在堆中創建新對象,而不管其字元串值是否相等,是否有必要創建新對象,從而加重了程序的負擔。
另一方面, 要注意: 我們在使用諸如String str = "abc";的格式定義類時,總是想當然地認為,創建了String類的對象str。擔心陷阱!對象可能並沒有被創建!而可能只是指向一個先前已經創建的對象。只有通過new()方法才能保證每次都創建一個新的對象。由於String類的immutable性質,當String變數需要經常變換其值時,應該考慮使用StringBuffer類,以提高程序效率。
java中內存分配策略及堆和棧的比較
2.1 內存分配策略
按照編譯原理的觀點,程序運行時的內存分配有三種策略,分別是靜態的,棧式的,和堆式的.
靜態存儲分配是指在編譯時就能確定每個數據目標在運行時刻的存儲空間需求,因而在編譯時就可以給他們分配固定的內存空間.這種分配策略要求程序代碼中不允許有可變數據結構(比如可變數組)的存在,也不允許有嵌套或者遞歸的結構出現,因為它們都會導致編譯程序無法計算準確的存儲空間需求.
棧式存儲分配也可稱為動態存儲分配,是由一個類似於堆棧的運行棧來實現的.和靜態存儲分配相反,在棧式存儲方案中,程序對數據區的需求在編譯時是完全未知的,只有到運行的時候才能夠知道,但是規定在運行中進入一個程序模塊時,必須知道該程序模塊所需的數據區大小才能夠為其分配內存.和我們在數據結構所熟知的棧一樣,棧式存儲分配按照先進後出的原則進行分配。
靜態存儲分配要求在編譯時能知道所有變數的存儲要求,棧式存儲分配要求在過程的入口處必須知道所有的存儲要求,而堆式存儲分配則專門負責在編譯時或運行時模塊入口處都無法確定存儲要求的數據結構的內存分配,比如可變長度串和對象實例.堆由大片的可利用塊或空閑塊組成,堆中的內存可以按照任意順序分配和釋放.
2.2 堆和棧的比較
上面的定義從編譯原理的教材中總結而來,除靜態存儲分配之外,都顯得很呆板和難以理解,下面撇開靜態存儲分配,集中比較堆和棧:
從堆和棧的功能和作用來通俗的比較,堆主要用來存放對象的,棧主要是用來執行程序的.而這種不同又主要是由於堆和棧的特點決定的:
在編程中,例如C/C++中,所有的方法調用都是通過棧來進行的,所有的局部變數,形式參數都是從棧中分配內存空間的。實際上也不是什麼分配,只是從棧頂向上用就行,就好像工廠中的傳送帶(conveyor belt)一樣,Stack Pointer會自動指引你到放東西的位置,你所要做的只是把東西放下來就行.退出函數的時候,修改棧指針就可以把棧中的內容銷毀.這樣的模式速度最快, 當然要用來運行程序了.需要注意的是,在分配的時候,比如為一個即將要調用的程序模塊分配數據區時,應事先知道這個數據區的大小,也就說是雖然分配是在程序運行時進行的,但是分配的大小多少是確定的,不變的,而這個"大小多少"是在編譯時確定的,不是在運行時.
堆是應用程序在運行的時候請求操作系統分配給自己內存,由於從操作系統管理的內存分配,所以在分配和銷毀時都要佔用時間,因此用堆的效率非常低.但是堆的優點在於,編譯器不必知道要從堆里分配多少存儲空間,也不必知道存儲的數據要在堆里停留多長的時間,因此,用堆保存數據時會得到更大的靈活性。事實上,面向對象的多態性,堆內存分配是必不可少的,因為多態變數所需的存儲空間只有在運行時創建了對象之後才能確定.在C++中,要求創建一個對象時,只需用 new命令編制相關的代碼即可。執行這些代碼時,會在堆里自動進行數據的保存.當然,為達到這種靈活性,必然會付出一定的代價:在堆里分配存儲空間時會花掉更長的時間!這也正是導致我們剛才所說的效率低的原因,看來列寧同志說的好,人的優點往往也是人的缺點,人的缺點往往也是人的優點(暈~).
2.3 JVM中的堆和棧
JVM是基於堆棧的虛擬機.JVM為每個新創建的線程都分配一個堆棧.也就是說,對於一個Java程序來說,它的運行就是通過對堆棧的操作來完成的。堆棧以幀為單位保存線程的狀態。JVM對堆棧只進行兩種操作:以幀為單位的壓棧和出棧操作。
我們知道,某個線程正在執行的方法稱為此線程的當前方法.我們可能不知道,當前方法使用的幀稱為當前幀。當線程激活一個Java方法,JVM就會在線程的 Java堆棧里新壓入一個幀。這個幀自然成為了當前幀.在此方法執行期間,這個幀將用來保存參數,局部變數,中間計算過程和其他數據.這個幀在這里和編譯原理中的活動紀錄的概念是差不多的.
從Java的這種分配機制來看,堆棧又可以這樣理解:堆棧(Stack)是操作系統在建立某個進程時或者線程(在支持多線程的操作系統中是線程)為這個線程建立的存儲區域,該區域具有先進後出的特性。
每一個Java應用都唯一對應一個JVM實例,每一個實例唯一對應一個堆。應用程序在運行中所創建的所有類實例或數組都放在這個堆中,並由應用所有的線程共享.跟C/C++不同,Java中分配堆內存是自動初始化的。Java中所有對象的存儲空間都是在堆中分配的,但是這個對象的引用卻是在堆棧中分配,也就是說在建立一個對象時從兩個地方都分配內存,在堆中分配的內存實際建立這個對象,而在堆棧中分配的內存只是一個指向這個堆對象的指針(引用)而已。
❻ 串是一種特殊的線性表,其特殊性表現在
數據元素是一個字元
❼ 關於串的問題
1,B
2,
3,C
5,A
❽ c語言數據結構(順序存儲字元串,判斷字元串是否對稱,藉助鏈棧)
#include <stdio.h>
#include <malloc.h>
#include <conio.h>
typedef struct SNode
{
int data;
struct SNode *next;
}SNode,*LinkStack;
int Push(LinkStack &top,char e)
{
SNode *q;
q=(LinkStack)malloc(sizeof(SNode));
if(!q)
{
printf("\nOverflow!");
return 0;
}
q->data=e;
q->next=top->next;
top->next=q;
return 1;
}
char Pop(LinkStack &top,char &e)
{
SNode *q;
if(!top->next)
{
return 0;
}
q=top->next;
top->next=q->next;
e=q->data;
free(q);
return e;
}
char GetTop(LinkStack top,char &e)
{
e=top->next->data;
return e;
}
int Check(char * ch)
{
int i,m,flg=0;
char *c;
char temp;
SNode *top;
top=(LinkStack)malloc(sizeof(SNode));
top->next=NULL;
for(m=1;ch[m]!='\0';m++);
for(i=0;i<m/2;i++)
{
Push(top,ch[i]);
}
if(m%2)
i++;
while(i<m)
{
if(ch[i]==GetTop(top,temp))
Pop(top,temp);
else
return 0;
i++;
}
return 1;
}
void main()
{
char c[40];
printf("輸入字元串: ");
scanf("%s",c);
if(Check(c))
printf("\n字元串對稱");
else
printf("\n字元串不對稱");
getch();
}
❾ 數據結構串的基本操作的實現
希望下面這個程序符合你的要求,呵呵#include<iostream>#include<malloc.h>#define OK 1#define ERROR 0using namespace std;typedef struct{ char *ch; int length; //串的長度}HString;//========= int StrAssign(HString &T){//生成一個串常量 int j; cout<<"請輸入串T的長度:"<<endl; cout<<"T.length="; cin>>T.length; cout<<"請輸入串:"<<endl; if(!T.length){T.ch=NULL;T.length=0;} else{ if(!(T.ch=(char*)malloc(T.length*sizeof(char)))) return ERROR; for(j=0;j<T.length;j++) cin>>T.ch[j]; } return OK;} //串的鏈接======= int Concat(HString &T){ int i,j; HString S1; HString S2; if(T.ch) free(T.ch); StrAssign(S1); StrAssign(S2); if(!(T.ch=(char*)malloc((S1.length+S2.length)*sizeof(char)))) return ERROR; for(i=0;i<S1.length;i++) T.ch[i]=S1.ch[i]; T.length=S1.length+S2.length; for(i=S1.length,j=0;i<T.length,j<S2.length;i++,j++) T.ch[i]=S2.ch[j]; return OK;} //串的插入======== int StrInsert(HString &T) //在串的T第pos個字元之前插入串S{ int i,j,k,pos; HString S; cout<<"請輸入串S的長度:"; cin>>S.length; cout<<"請輸入串S:"<<endl; if(!(S.ch=(char*)malloc(S.length*sizeof(char)))) return ERROR; for(i=0;i<S.length;i++) cin>>S.ch[i]; cout<<"請輸入插入的位置:"; cin>>pos; if(pos<1||pos>T.length+1) return ERROR; if(S.length) { if(!(T.ch=(char*)realloc(T.ch,(T.length+S.length)*sizeof(char)))) return ERROR; for(i=T.length-1;i>=pos-1;i--) T.ch[i+S.length]=T.ch[i]; for(j=pos-1,k=0;j<=pos+S.length-2,k<S.length;j++,k++) T.ch[j]=S.ch[k]; T.length+=S.length; } return OK;} //串的輸出========== void print(HString &T){ int i; for(i=0;i<T.length;i++) cout<<" "<<T.ch[i];} //主函數=========== void main(){ cout<<"=============串的堆分配存儲表示=============="<<endl; HString T; StrAssign(T); cout<<"您輸入有效串為:"<<endl; print(T); cout<<endl; cout<<"==========串連接操作=========="<<endl<<endl; Concat(T); cout<<"連接後的串為:"<<endl; print(T); cout<<endl; cout<<"==========串插入操作=========="<<endl<<endl; StrInsert(T); cout<<"插入後的串為:"<<endl; print(T); cout<<endl; }
❿ 串和字元的存儲結構有什麼不同串通常採用什麼存儲結構
1、所有數據在計算機中,存放的均是二進制串,只有不同類型的數據在讀寫時按照讀寫的方式不同;
2、c++中的string類型是類對象,字元串是
char類型的數組,char則是更基本的類型。如果不懂類對象與char數組的區別的話,可以取了解一下c++的類。對於字元串與字元的區別的話,那就基本是批發與零售的區別了。