1. 程式人生 > >1.線性表建立、元素插入、元素刪除、線性表合併(原創)

1.線性表建立、元素插入、元素刪除、線性表合併(原創)

參考書籍:資料結構(C語言版)--嚴蔚敏(清華大學出版社)

最近在學資料結構,選用以上參考資料,書中的例子只是一個程式設計參考,並不能直接使用,這裡我給出完整例項(程式設計

思想與書本保持一致)。

實際問題1:假設利用兩個線性表LA和LB分別表示兩個集合A和B(即線性表中的資料元素即為集合中的成員),現要求

一個新的集合A=A U B(並運算)。這就要求:擴大線性表LA,將存在於線性表LB中而不存在與線性表LA中的資料元素

插入到線性表LA中去。--只要從線性表LB中依次取得每個資料元素,並依值線上性表LA中進行查訪,若不存在則插入之。

實際問題2:已知線性表LA和LB的資料元素按值非遞減有序排列,現在要求將LA和LB併為一個新的線性表LC,且LC中的

資料元素仍按值非遞減有序排列。

實際問題1用演算法函式1解決,實際問題2用演算法函式2解決,因為兩個問題用到的函式很多是一樣的,所以寫在一個程式檔案中。

其中:需要注意易錯點:

(1)插入操作函式輸入的插入位置是位序,不是陣列下標,並且是插入到位序之前,如果要在位序之後,必須再加1(記得輸入合法性判斷)

(2)插入操作函式不可以對空表進行操作,因為L.length不能為0,至少為1,也就是需要一個函式來初始化線性表第一個元素

(3)元素定位函式必須對端點值進行單獨判斷

(4)插入操作函式和刪除操作函式都是複雜度比較高的操作,因為涉及後續所有元素的向前/向後移位,效率比較低

(5)對於線性表(如陣列)的指標操作更快捷,但是記得次數判斷時多使用末尾元素指標作為迴圈終止條件

(6)合併已排序線性表函式ListMergeOrder_better使用指標操作,用複製代替ListMergeOrder函式的插入操作(需要遍歷),效率更高

#include<iostream>
using namespace std;

#define Size_InitList  100
#define Increment_List 10
int la[Size_InitList] = { 1, 2, 6, 7 };
int lb[Size_InitList] = { 4, 5, 6, 7, 8, 9, 10, 11 };
typedef struct{
	int* elem;
	int length;
	int listsize;
}Sqlist;

void ListInit(Sqlist &L);
int ListFirstDataInit(Sqlist &L, int ListFirstData);
int ExistenceElem(Sqlist L, int e);
int ListInsert(Sqlist &L, int i, int insertdata);
int ListDelete(Sqlist &L, int i, int &deletedata);
void ListDisplay(Sqlist L);
void UnionList(Sqlist &La, Sqlist Lb);
void ListMergeOrder(Sqlist &Lc, Sqlist La, Sqlist Lb);
void ListMergeOrder_better(Sqlist &Lc, Sqlist La, Sqlist Lb);
int main(){
	Sqlist La, Lb, Lc;
	ListInit(La);
	ListInit(Lb);
	ListInit(Lc);
	for (int i = 0; i < 4; i++){
		if (i == 0){
			ListFirstDataInit(La,la[i]);
		}
		ListInsert(La, i + 1, la[i]);//插到第i個元素之後,為i+1
	}
	for (int i = 0; i < 8; i++){
		if (i == 0){
			ListFirstDataInit(Lb, lb[i]);
		}
		ListInsert(Lb, i + 1, lb[i]);
	}
	/*******解題演算法1:合併線性表La和Lb(相同元素不重複)*********/
	//UnionList(La,Lb);
	//ListDisplay(La);

	/*******解題演算法2:合併線性表La和Lb(相同元素重複),並且重新排序********/
	ListMergeOrder_better(Lc, La, Lb);
	ListDisplay(Lc);
	
	return 0;
}

void ListInit(Sqlist &L){
	L.elem = (int*)malloc(Size_InitList*sizeof(int));
	if (!L.elem){
		cout << "Memory allocation fail!" << endl;
		exit(1);
	}
	L.length = 0;
	L.listsize = Size_InitList;
}
int ListFirstDataInit(Sqlist &L, int ListFirstData){
	L.elem[0] = ListFirstData;

	return 0;
}
/************************************  ListInsert  ***********************************
//線性表元素插入
//第一:線性表不能為空(無法初始化第一個元素,因為L.length不能為0);
//第二:這裡的i是位序,如果插到第i元素前則為i,如果插到第i元素後則為i+1
//第三:時間複雜度:O(L.length)
*/
int ListInsert(Sqlist &L, int i, int insertdata){
	if (i<1 || i>L.length + 1){//L.length+1:可以插入到最後一個元素的後面
		cout << "InserList input error!" << endl;
		return 0;
	}
	if (L.length >= L.listsize){
		int* newbase = (int*)realloc(L.elem, (L.listsize + Increment_List)*sizeof(int));
		if (!newbase){//分配失敗
			exit(1);
		}
		L.elem = newbase;
		L.listsize += Increment_List;
	}
	int* remark = &L.elem[i - 1];//注意:remark所指的元素是開始往後移動的元素,所以p>=remark取到等號
	int* p = &L.elem[L.length - 1];
	for (; p >= remark; p--){
		*(p + 1) = *p;
	}
	*remark = insertdata;
	++L.length;

	return 0;
}

/********************************  ListDelete  ******************************************
//線性表元素刪除
//時間複雜度:O(L.length)
*/
int ListDelete(Sqlist &L, int i, int &deletedata){
	if (i<1 || i>L.length){
		cout << "ListDelete:ListDelete Input error!" << endl;
		return 0;
	}
	int* last = &L.elem[L.length - 1];
	int* p = &L.elem[i - 1];
	deletedata = *p;
	for (; p < last; p++){
		*p = *(p + 1);
	}
	--L.length;

	return 0;
}
void ListDisplay(Sqlist L){
	for (int i = 0; i < L.length; i++){
		cout << L.elem[i] << ' ';
	}
	cout << endl;
}
/*******************************  ExistenceElem  *******************************************
//判斷線性表中元素的存在性
//查詢L中是否存在元素e,不存在返回0,否則返回非0值(返回位序);
//時間複雜度:O(L.length)
*/
int ExistenceElem(Sqlist L, int e){
	for (int i = 0; i < L.length; i++){
		if (L.elem[i] == e){
			return i + 1;
		}
	}
	return 0;
}

int ElemLocate(Sqlist L, int elem){//e為要查詢的元素,函式返回實際要插入的位序
	if (elem <= L.elem[0]){//端點值單獨判斷
		return 1;
	}
	if (elem >= L.elem[L.length - 1]){//端點值單獨判斷
		return L.length + 1;
	}
	for (int i = 0; i < L.length - 1; i++){
		if (elem > L.elem[i] && elem <= L.elem[i + 1]){
			return i + 2;
		}
		else continue;
	}
	return 0;
}
/************************************* UnionList ***********************************
//演算法函式1:合併線性表La和Lb(相同元素不重複)
//將Lb中與La不同的元素插入到La中去
//時間複雜度:O(La.length * Lb.length), ExistenceElem執行時間和表長成正比,ListInsert和表長無關
*/
void UnionList(Sqlist &La, Sqlist Lb){
	for (int i = 0; i < 8; i++){
		if (!ExistenceElem(La, Lb.elem[i])){//只需在最開始的La裡面查詢(Lb中資料插入La中為“追加”),不會影響Lb中重複的元素
			ListInsert(La, La.length + 1, Lb.elem[i]);//將Lb中元素追加到La後面,每次La.length都會更新
		}
	}
}

/************************************  ListMergeOrder *********************************
//演算法函式2:合併線性表La和Lb(相同元素重複),並且重新排序
//注意:Lc可以為空,無需初始化第一個元素
//時間複雜度:O(La.length + Lb.length)
*/
void ListMergeOrder(Sqlist &Lc, Sqlist La, Sqlist Lb){
	int remark;
	for (int i = 0; i < La.length; i++){
		Lc.elem[i] = La.elem[i];
		++Lc.length;
	}
	for (int i = 0; i < Lb.length; i++){
		remark = ElemLocate(Lc, Lb.elem[i]);
		ListInsert(Lc, remark, Lb.elem[i]);
	}
}

/***************************** ListMergeOrder_better *******************************************
//演算法函式2(效率更高):合併線性表La和Lb(相同元素重複),並且重新排序
//注意要對Lc分配空間,否則不能保證資料是否放得下
//因為這裡是對有序線性表合併,所以要比之前的 ListUnion 時間複雜度更低
//時間複雜度:O(La.length + Lb.length)
*/
void ListMergeOrder_better(Sqlist &Lc, Sqlist La, Sqlist Lb){
	int i=0,j=0, Len_a, Len_b;
	int* pa = La.elem;
    int* pb = Lb.elem;
	int* pa_last = La.elem + La.length - 1;
	int* pb_last = Lb.elem + Lb.length - 1;
	Lc.length = La.length + Lb.length;
	int* pc =Lc.elem= (int*)malloc(Lc.length*sizeof(int));//保證Lc空間足夠
	if (!pc){
		cout << "ListMergeOrder_better:Memory allocation fail!" << endl;
		exit(1);
	}
	while (pa <= pa_last&&pb <= pb_last){//使用末尾元素指標作為次數判斷更加方便
		if (*pa <= *pb){//如果將*pa==*pb分開,並且兩個元素中只插入一個,就變成了ListUnion函數了
			            //只是這裡時間複雜度更低,因為使用複製代替了元素查詢
			*pc++ = *pa++;
		}
		else{
			*pc++ = *pb++;
		}
	}	
	while (pa <= pa_last){
		*pc++ = *pa++;
	}
	while (pb <= pb_last){
		*pc++ = *pb++;
	}
}