數據結構-線性表順序存儲結構上的基本運算實現(詳解)


查找操作

算法思想

查找運算可采用順數查找,即從第一個元素開始,依次將表中的的元素與所要查找的元素進行比較,如果相等,則查找成功。如果查找成功輸出相應的提示信息,反之也給予相應的提示信息。

算法實現

#include<stdio.h>  
#include<stdlib.h>  
#define MAX 20  

  
typedef struct{  
    int *elem;  
    int length;  
    int listsize;  
}SqList;  
void CreatList(SqList &L)  
{//建立一個線性表  
    L.elem=(int *)malloc(MAX * sizeof(int));  //動態數組空間分配
    if(!L.elem)  //! 是“非”的意思,如果沒被分配空間 就結束程序。 
        return;//exit(0)  
    L.listsize=MAX;  
    printf("輸入表的長度:");  
    scanf("%d",&L.length);  
    printf("輸入%d個數:",L.length);  
    for(int i=0;i<L.length;i++)  
        scanf("%d",&L.elem[i]);  
}  
void Traverse(SqList L){  
    //遍歷  
    printf("表中數據為:");  
    for(int i=0;i<L.length;i++)  
        printf("%3d",L.elem[i]);  
    printf("\n");  
}  
void LocateElem(SqList L,int e){  
    //查找  
    int i;  
    printf("輸入查找的元素:");  
    scanf("%d",&e);  
    for(i=0;i<L.length;i++)  
    {  
        if(L.elem[i]==e){  
            printf("查找成功,查找元素為%d",L.elem[i]);  
            printf("\n");  
            return;// =return 0;相當於是退出程序  
        }  
    }  
    printf("查找失敗");  
    printf("\n");  
}  

int main(void)
{
	SqList L;  
    CreatList(L);  
    Traverse(L);  
    LocateElem(L,1);  
   
      
	return 0;
} 

運行演示

算法小結

  • 首先要先創建一個線性表。
  • 第二就是對線性表進行遍歷,查找后輸出。
  • L.elem=(int *)malloc(MAX * sizeof(int)); 動態數組空間分配, 因為要靜態的數組的空間只要定義好了之后就不能再被分配了。我們這里是數組的長度是我們輸入來實現的,所以是動態的。 (MAX * sizeof(int)) 這劇代碼返回的是整數型的數據,所以指針用的是 int * 定義指針 的原因是因為 malloc函數只能返回第一個字節的地址,但是這個地址是沒有含義的,為什么說是沒有意義的呢?因為只知道這個地址,但是后面有幾個字節是不知道。
  • void CreatList(SqList &L) &代表引用,使用&后所傳參數會改變,仔細觀察你會發現,凡是對鏈表有改動的,如刪除函數,增加函數,形參前面都會有&符號,那是因為調用了這個函數后,所傳鏈表(結構體)會改變。

插入操作

算法思想

查找運算可采用順數查找,即從第一個元素開始,依次將表中的的元素與所要查找的元素進行比較,如果相等,則查找成功。如果查找成功輸出相應的提示信息,反之也給予相應的提示信息。

算法實現

#include<stdio.h>
#include<stdlib.h>
#define MAX 20


typedef struct{
	int *elem;
	int length;
	int listsize;
}SqList;
void CreatList(SqList &L)
{//建立一個線性表
	L.elem=(int*)malloc(MAX *sizeof(int));
	if(!L.elem)
		return;//exit(0)
	L.listsize=MAX;
	printf("輸入表的長度:");
	scanf("%d",&L.length);
	printf("輸入%d個數:",L.length);
	for(int i=0;i<L.length;i++)
		scanf("%d",&L.elem[i]);
}
void Traverse(SqList L){
	//遍歷
	printf("表中數據為:");
	for(int i=0;i<L.length;i++)
		printf("%3d",L.elem[i]);
	printf("\n");
}


void ListInsert(SqList &L)  //改變鏈表中元素,有&符號 
{//插入元素及其要插入的位置  
    int i;
	int e;  
    printf("輸入要插入位置及元素\n");  
    scanf("%d%d",&i,&e);  
    printf("在順序線性表中第%d個位置之前插入新的元素%d。\n",i,e);//在順序線性表L中第i個位置之前插入新的元素e,  
    if(i<1||i>L.length+1) return; //i的合法位置為1<=i<=ListLength(L)+1  
    int *p,*q;  
    q=&(L.elem[i-1]);  
    for(p=&(L.elem[L.length-1]);p>=q;--p)*(p+1)=*p;  
        *q=e;  
    
    ++L.length;  
    return;  
}  



int main(){
 SqList L;  
    CreatList(L);  
    Traverse(L);    
    ListInsert(L);
    Traverse(L);  
    
	return 0;
}

運行演示

算法小結

  • q=&(L.elem[i-1]); 表示從鏈表的第i個元素開始一直到最后一個元素往后移一位.
  • p=&L.elem[L.length-1] 意思是p賦初值為鏈表的最后一個元素地址,p>=q表示循環知道p<q的時候結束,--p是使p指針的指向往前移一位.

刪除操作

算法思想

用順序表作為線性表的存儲結構時,由於結點的物理順序必須和結點的邏輯順序保持一致,因此當需要刪除第i個元素時,必須將表中位置相似i+1,i+2,...,n-1,n上的節點,依次前移到位置i,i+1,...n-1(其中n為L的表長度)。

算法實現

#include<stdio.h>
#include<stdlib.h>
#define MAX 20


typedef struct{
	int *elem;
	int length;
	int listsize;
}SqList;
void CreatList(SqList &L)//對鏈表有改動 形參有&符號
{//建立一個線性表
	L.elem=(int*)malloc(MAX *sizeof(int));
	if(!L.elem)
		return;//exit(0)
	L.listsize=MAX;
	printf("輸入表的長度:");
	scanf("%d",&L.length);
	printf("輸入%d個數:",L.length);
	for(int i=0;i<L.length;i++)
		scanf("%d",&L.elem[i]);
}
void Traverse(SqList L){
	//遍歷
	printf("表中數據為:");
	for(int i=0;i<L.length;i++)
		printf("%3d",L.elem[i]);
	printf("\n");
}



int makesureElem(SqList L,int e)//這里需要用到兩個參數,首先是 鏈表,另外一個就是刪除的元素(表中)
{
	int i;
//確定要刪除的元素
	for(i=0;i<L.length;i++)
	{
		if(L.elem[i]==e)
		{
			printf("要刪除的元素 位置為 %d",i+1);
			printf("\n");
			return (i+1);
		
		}
	}
	
	printf("元素不存在");
	printf("\n");	return 0;
		
}

int ListDelete(SqList &L){ //對鏈表有改動 形參有&符號
	//刪除元素
	int i;
	int e;
	printf("輸入要刪除的元素");
	scanf("%d",&e);
    i=makesureElem(L,e);
	if((i<1)||(i>L.length)) return 0;//i的合法值為1<=i<=ListLength(L)+1
	else{
		int* p,* q;
		p=&(L.elem[i-1]);//取回鏈表中每一個元素的地址賦值給p,則指針p指向的內容就是*p
		e=*p;//找到表中要被刪除的元素“e”(也就是*p)
		q=L.elem+L.length-1;//尾元素的位置,更多解釋看說明
		for(++p;p<=q;++p)//初始化指針並移動,更多解釋看說明
           {
               *(p-1)=*p; //被刪除元素之后的元素左移
           }
          
		--L.length;
		printf("元素被刪除");
	}
return 0;
	        
}	
int main(){
	SqList L;
	CreatList(L);
	Traverse(L);
	ListDelete(L);
	Traverse(L);
	
	return 0;
}

運行演示

算法小結

  • q=L.elem+L.length-1順序存儲結構(實際上就是數組)中,l.elem表示線性表l中存儲數據(數組)的基地址(起始地址)這個也就是一開始建立順序表的原因,其實本質上就是進行了用程序模擬了c語言中的數組,之所一開始 int* elem 的原因就是 和 數組中的的定義一樣,例如數組a[2] a就是 a[0]的地址,即 a == &a[0]l.length是表的長度(數據元素個數),q是指針通過上式計算后指向尾元素和數組的情況一樣,例如:int a[10],*p=a;//p指向第一個元素
    p=a+1;//指向第二個元素.
    例如:p=a+10-1;指向最后一個數組元素,即a[9].

  • for(++p;p<=q;++p)p是一個被初始化過的指針,按上面代碼應該指向某類型的數組,為超表達方便,數組記為x(i)。for循環首先把p從當前位置x(k)移動到x(k+1)作為初值,只要指針沒到q指向的位置,就繼續循環,循環每次遞增一個數據。循環體將數組當前位置數據拷貝到前一個位置。總之,初始時,如果p指向x(m),q指向x(n),n應該大於m。最終運行結果是x(m)=x(m+1)=...=x(n)。

  • 刪除操作中函數的返回值是int型的,當然也可以是void的型的,如果是void型的話,把return 0;寫成return;就好了。

順序表合並

有兩個順序表LA 和 LB,其元素均為非遞減有序排列,可設兩個指針i、j 分別指向表LA 和 LB 中的元素,若LA.elem[i]>LB.elem[j],則當前先將LB.elem[j]插入到表LC中,若LA.elem[i]<=LB.elem[j],則當前先將LA.elem[i]插入到表LC中,如此進行下去,其中一個表被掃描完畢,然后再將未掃描完的表中剩余的所有元素放到表LC中。其中個一個表被掃描完畢,然后再將未掃描完的表中剩余的所有元素放到表LC中。

算法演示

#include<stdio.h>
#include<stdlib.h>
#define MAX 20


typedef struct{
	int *elem;
	int length;
	int listsize;
}SqList;
void CreatList(SqList &L)
{//建立一個線性表
	L.elem=(int*)malloc(MAX *sizeof(int));
	if(!L.elem)
		return;//exit(0)
	L.listsize=MAX;
	printf("輸入La表的長度:");
	scanf("%d",&L.length);
	printf("輸入%d個數:",L.length);
	for(int i=0;i<L.length;i++)
		scanf("%d",&L.elem[i]);
}


//創建第二個表
void CreatList2(SqList &L2)
{
	L2.elem=(int*)malloc(MAX *sizeof(int));
	if(!L2.elem)
		return;//exit(0)
	L2.listsize=MAX;
	printf("輸入Lb表的長度:");
	scanf("%d",&L2.length);
	printf("輸入%d個數:",L2.length);
	for(int i=0;i<L2.length;i++)
		scanf("%d",&L2.elem[i]);
} 

void Traverse(SqList L){
	//遍歷
	printf("La中數據為:");
	for(int i=0;i<L.length;i++)
		printf("%3d",L.elem[i]);
	printf("\n");
}

void Traverse2(SqList L2){
	//遍歷
	printf("Lb表中數據為:");
	for(int i=0;i<L2.length;i++)
		printf("%3d",L2.elem[i]);
	printf("\n");
}

void Traverse3(SqList L3){
	//遍歷
	printf("組合表Lc中數據為:");
	for(int i=0;i<L3.length;i++)
		printf("%3d",L3.elem[i]);
	printf("\n");
}


void MergeList_Sq(SqList la,SqList  lb,SqList  &lc)//增加的是lc的操作,前面的幾個例子也說名了,只要牽扯到改變表中數據
 												   //就用引用符號&  
{
	int* pa = la.elem; // 對順序表中第一個元素的地址進行初始化  
	int* pb = lb.elem;
	lc.listsize = lc.length = la.length + lb.length; //Lc表初始化大小 
	int* pc = lc.elem = (int*)malloc(MAX *sizeof(int)); //請求系統進行動態內存分配 
	if (!lc.elem)
	{
		return;//存儲分配失敗 
	}
	int* pa_last = la.elem + la.length - 1; //定義La 順序表中末尾元素的地址,而且在線性表中地址都是遞增,且遞增幅度不變 
	int* pb_last = lb.elem + lb.length - 1; //看下面的圖就清晰了 
	
	while (pa <= pa_last && pb <= pb_last) //其實都是比較的地址 ,還有就是注意<= 不要寫成<
	{
		if (*pa <= *pb)//對於指針指向的內容作比較 
		{
			*pc++ = *pa++; // 等價於 *pc = *pa; *pa++;  *pc++;注意此處的 *pa++ 不可以寫成* (++pa),說明看算法小結 
		}
		else
		{
			*pc++ = *pb++;
		}
	}
	
	while (pa <= pa_last) *pc++ = *pa++;//插入La的剩余元素 
	while (pb <= pb_last) *pc++ = *pb++;
	
	
}


int main(){
 SqList L,L2,L3;  
    CreatList(L);  
    Traverse(L);    
    CreatList2(L2);  
    Traverse2(L2);  
    MergeList_Sq(L,L2,L3);
    Traverse3(L3);  
	return 0;
}

運行演示

算法小結

  • int* pa_last = la.elem + la.length - 1; 這段代碼就是求表中最后一個元素的地址,而地址是按一定的幅度遞增的,如下圖

  • while (pa <= pa_last && pb <= pb_last) 這里面的符號,一定不要寫錯了,一開始就是因為<= 寫成了< 導致程序運行失敗,並沒有進行排序
    就直接存到Lc中了。

  • *pc++ = *pa++; 等價於 *pc = *pa; pa++; pc++;注意此處的 pa++ 不可以寫成 (++pa),因為是先把pa 賦值給 pc ,所以不能寫反了,如果寫反了就是先p+1 ,然后才賦值給pc,這樣程序邏輯會出錯。

線性順序表優缺點

優點
1.無須為表示結點間的邏輯關系而增加額外的存儲空間(因為邏輯上相鄰的元素其存儲的物理位置也是相鄰的)。
2.可以方便的隨機存取表中的任一個元素。
缺點
1.插入和刪除運算不方便,除表尾的位置外,在表的其他位置上進行插入或者刪除操作都必須移動大量的結點,其效率低。
2.由於順序表要求占用連續的存儲空間,存儲分配只能預先進行靜態分配。因為當表變化較大時,難以確定合適的存儲規模。若按可能達到最大長度預先分配表空間,則可能造成一波分空間長期閑置而得不到充分利用;若事先對表長估計不足,則插入操作可能使表長超過預先分配的空間而造成溢出。

參考文獻


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM