C 鏈表大例題

帶頭結點鏈表

在這裏插入圖片描述
其中帶頭結點鏈表第一個節點只有數據域有意義,存的是第一個有效節點的地址
而不帶頭結點鏈表有一個頭指針 即head

帶頭結點鏈表是最少4字節 而不帶頭結點鏈表最少是4字節 所以不帶頭比帶頭存儲數據要多

帶頭結點鏈表大例題需求說明:
1.實現對屏幕上的點座標進行管理(也可以用數組實現)
2.屏幕點座標由行座標和列座標組成,且座標取值1-25,列座標1-80
3.實現點座標的錄入 插入 刪除 輸出 輸出 升序排序
使用如下結構體

typedef struct point{
	int row;
	int col:
	struct point *next;
}point;

空鏈在這裏插入圖片描述next 指向NULL
point head1 = {0}: 這就是空鏈

inputpoints(point *head){
	int row;
	int col;
	POINT point;
	printf("輸入一個點座標(輸入0截至)")
	scanf("%d%d",&row,&col);
	while(row&&col//判斷是否爲0){//處理row和col
		point.row = row;
		point.col =col;
		point.next =NULL;
		head->next = &point;
}
	point *p;
	p =(point*)malloc(sizeof(point));
	p->next =NULL;//使最後結點所指向的地址爲NULL 否則爲垃圾數據
	head->next = p;//將p的值(即剛申請空間的首地址)賦值給head所指向的實例的next成員}
	int main(){
	Point pointList1 = {0}//創建空鏈
	Point pointList2= {0}inputpoints(&pointList1)return 0;

}

代碼中的while語句中 處理row和col乍一看是對的 其實有很大的錯誤,因爲point是inputpoints()函數的局部變量,當程序執行返回到主函數後,pointlist的next,將指向一個已經釋放了的空間
所以應該如下處理row 與col

	viod inputpoints(point *head){
	int row;
	int col;
	POINT *p =NULL
	printf("輸入一個點座標(輸入0截至)")
	scanf("%d%d",&row,&col);
	while  (row &&col){
	p = (point *)malloc(sizeof(POINT));
	p->row =row;
	p->col =col;
	p->next =NULL;
	head ->next=p;
	printf("輸入一個點座標(輸入0 則截至錄入)");
	scanf("%d%d",&row,&col);
}

在這裏插入圖片描述
運行第二次後,會發生錯誤,導致第一次申請的空間變得無效,且不可釋放,即內存泄漏
在這裏插入圖片描述
所以要用末節點來進行鏈接
在這裏插入圖片描述

POINT *tail
if(NULL ==head->next){
	//空鏈時
	head->next =p;
}else{
	//非空鏈
	//需要先找到末節點
	//for (tail =p->next;tail->next!=NULL;tail = tail->next){
	tail->next =p//tail將指向末節點,但這樣隨着鏈表的增加,時間複雜度越來越高,可以在下面加上如下語句,即可去掉for循環,可以使複雜度大大減}
	tail = p;//使tail始終指向末節點
	printf("請輸入一個點座標" );
	sacnf("%d%d",&row,&col);
	}

showPointList()函數用來顯示點,無返回值且需要一個參數,POINT head
由於不需要更改數值 所以不加*,反之則相反

head佔12個字節
在這裏插入圖片描述

void showonepoint(POINT point){
	printf("%d%d",&row,&col);
}
void showPointList(Point head){
	POINT *p;
	printf("當前點座標如下");
	for(p =head.next;p;p =p->next){
		showonepoint(*p)//p只是四字節的指針,形參需要12字節point的實例,*p就爲p所指向的值}}

釋放鏈表所有節點
定義void destoryPointList(); 無返回值且一個參數&pointList
釋放方法:存在兩種
1.從後向前釋放
2.從前向後釋放
當從後向前釋放時,僞代碼如下
destroyPointLIst(&pointList1){ point *tail; tail = head ->next; while(還有節點未釋放){ tail =找到末節點; free(tail); } }
若這樣在釋放第一個節點後,再次定位後仍會找到呢個剛被釋放了的空間;看下定位的代碼

for(tail =head->next;tail->next =NULL;tail=tail->next);

在這裏插入圖片描述
兩次都將tail指向了同一個空間

所以應該從前向後釋放
p =head =next爲在這裏插入圖片描述
而head ->next =p->next;會跨過釋放了的空間指向下一個空間
在這裏插入圖片描述
當執行到最後一個的時候就變成在這裏插入圖片描述
所以要讓&#1變爲NULL並free()p所指向的空間

destroyPointLIst(POINT *head)
destroyPointLIst(POINT *head){
	POINT *p ;
	for(p =head->next;p;p=head->next){
	head->next=p->next;
	free (p);
	}
	
}

插入部分:
insertPoint(); 應爲要插入指定鏈表 所以要傳遞鏈表的地址即&pointList,還要確定插入的新點座標和插入位置 參數爲(POINT *head)。
將用戶輸入的新點座標,插入到指定鏈表中,由用戶指定的點的前面,若用戶指定點不存在,則尾部追加;

void insertPoint(Point *head){
	//1.輸入新點座標
	//2.輸入指定點座標
	//3.查找該指定點的前驅節點
	//4.插入
	
	}
POINT *searchPrePoint(POINT head,Point point);//用來尋找指定點前驅的首地址{
	POINT *p;
	POINT *pre=NU;
	for(p =head.next;p&&!(p->row==point.row&&p->col==point.col);p=p->next){
	pre =p;
	}
	return pre;

}

上述函數的返回值有三種情況;
1.NULL,這意味着指定點爲第一個有效節點;
2,末節點,這意味着指定點根本不存在於鏈表中;
3,其他情況,是要找的點的前驅節點。

void insertPoint(POINT *head){
	int newrow;
	int newcol;
	POINT *newpoint;
	int oldRow;
	int oldcol;
	POINT oldpoint;
	//輸入新點座標
	printf("請輸入點座標");
	scanf("%d,%d",&newrow,&newcol);
	newPoint=(POINT*)malloc(sizeof(POINT));
	newPoint ->row =newRow;
	newPoint->col=newCol;
	newPoint->next =NULL;
	//2.輸入指定點座標
	printf("請輸入指定點座標")scanf("%d%d",&oldrow,&oldcol);
	oldpoint.row =oldrow;
	oldpoint.col =oldcol;
	//查找指定點前驅節點;
	prepoint =searchprepoint(*head,oldPoint);
	//插入
	if(prePoint ==NULL)//處理上述第一種情況,即需要將newpoint插入到頭節點後面!{
		newPoint->next =head->next;
		head->next =newPoint;
		}
		else if (prePoint->next==NULL){//指定點不存在,需要將newPoint追加到末節點的末尾
		newPoint->next=prePoint->next;
		prePoint->next=newPoint;}
		else{/ /其他情況
		newPoint->next=prePoint->next;
		prePoint->next=newPoint;}

}

上述處理三種寫法還可以繼續優化

//插入
if(prePoint==NULL;){
	prePoint =head;}
	newPoint->next =prePoint->next;
	prePoint->next =newPoint;

下面是全部代碼

#include <stdio.h>
#include <malloc.h>

typedef struct POINT {
	int row;
	int col;
	struct POINT *next;
}POINT;

typedef unsigned char boolean;
#define TRUE	1
#define FALSE	0

void inputPoints(POINT *head);
void showPoints(POINT head);
void destoryPoints(POINT *head);
POINT *searchPreviousPoint(POINT head, POINT point);
void insertPoint(POINT *head);
boolean removePoint(POINT *head);
void sortByRow(POINT head);

void sortByRow(POINT head) {
	POINT *p;
	POINT *q;
	POINT tmp;
	POINT *r;

	for (p = head.next; p; p = p->next) {
		for (q = p->next; q; q = q->next) {
			if (p->row > q->row) {
				tmp = *p;
				*p = *q;
				*q = tmp;

				r = p->next;
				p->next = q->next;
				q->next = r;
			}
		}
	}
}

boolean removePoint(POINT *head) {
	int row;
	int col;
	POINT point;
	POINT *pre;
	POINT *p;
	// 1、輸入要刪除的點座標(稱爲:指定點);
	printf("請輸入要刪除的點:");
	scanf("%d%d", &row, &col);
	point.row = row;
	point.col = col;
	// 2、查找指定點的前驅節點;
	pre = searchPreviousPoint(*head, point);
	// 3、分情況刪除。
	// pre有三種可能性的取值:NULL、pre->next爲NULL和其它
	if (pre == NULL) {
		pre = head;
	} else if (pre->next == NULL) {
		return FALSE;
	}
	p = pre->next;
	pre->next = p->next;
	free(p);

	return TRUE;
}
/*
	if (pre == NULL) {
		p = head->next;
		head->next = p->next;
		free(p);
	} else if (pre->next == NULL) {
		return FALSE;
	} else {
		p = pre->next;
		pre->next = p->next;
		free(p);
	}
 */

/**
 * 函數功能:
 * 1、輸入指定點;
 * 2、輸入新點;
 * 3、將新點插入到指定點的左側(前插法);若指定點不存在,
 * 		則,將新點追加到鏈表末尾。
 * @param head 指定鏈表的頭結點地址,即,head指向頭結點
 */
void insertPoint(POINT *head) {
	int oldRow;
	int oldCol;
	POINT old;
	int row;
	int col;
	POINT *p;
	POINT *pre;
	// 輸入指定點;
	printf("請輸入指定點座標:");
	scanf("%d%d", &oldRow, &oldCol);
	old.row = oldRow;
	old.col = oldCol;
	// 輸入新點;
	printf("請輸入新點座標:");
	scanf("%d%d", &row, &col);
	// 申請空間,並完成賦值;
	p = (POINT *) malloc(sizeof(POINT));
	p->row = row;
	p->col = col;
	p->next = NULL;
	// 查找指定點的前驅節點;
	pre = searchPreviousPoint(*head, old);
	// 插入。
	if (pre == NULL) {
		pre = head;
	}
	p->next = pre->next;
	pre->next = p;
}

/**
 * 查找指定點的前驅節點
 * @param  head  指定鏈表頭結點
 * @param  point 指定點
 * @return       分三種情況:
 * 1、若指定點是第一個有效節點,則,返回值爲NULL;
 * 2、若指定點是非第一個有效節點外的其它點,則,返回值爲
 * 		指定點的前驅節點的首地址;
 * 3、若指定點不存在於鏈表中,則,返回值不爲NULL,但其
 * 		next成員的值爲NULL!
 */
POINT *searchPreviousPoint(POINT head, POINT point) {
	POINT *result = NULL;
	POINT *p;

	for (p = head.next; p; p = p->next) {
		if (p->row == point.row && p->col == point.col) {
			return result;
		}
		result = p;
	}

	return result;
}

void destoryPoints(POINT *head) {
	POINT *p;

	while (head->next) {
		p = head->next;
		head->next = p->next;
		free(p);
	}
}

void showPoints(POINT head) {
	POINT *p;

	for (p = head.next; p; p = p->next) {
		printf("%d %d\n", p->row, p->col);
	}
}

void inputPoints(POINT *head) {
	int row;
	int col;
	POINT *p;
	POINT *tail = NULL;

	printf("請輸入點座標(若行或列值爲0,則,停止輸入):");
	scanf("%d%d", &row, &col);
	while (row && col) {
		p = (POINT *) malloc(sizeof(POINT));
		p->row = row;
		p->col = col;
		p->next = NULL;

		if (head->next == NULL) {
			head->next = p;
		} else {
			tail->next = p;
		}
		tail = p;

		printf("請輸入點座標(若行或列值爲0,則,停止輸入):");
		scanf("%d%d", &row, &col);
	}
}

/*
6 5
8 8
2 3
3 1
9 2
8 3
8 5
0 0
3 1
6 66
6 66
 */

int main() {
	POINT pointList1 = {0, 0, NULL};

	inputPoints(&pointList1);
	showPoints(pointList1);

	insertPoint(&pointList1);
	showPoints(pointList1);

	if (removePoint(&pointList1)) {
		showPoints(pointList1);
	} else {
		printf("刪除失敗,指定點不存在!\n");
	}

	sortByRow(pointList1);
	printf("排序後:\n");
	showPoints(pointList1);

	destoryPoints(&pointList1);

	return 0;
}

指導人—微易碼 教主
微易碼官網

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章