帶頭結點鏈表
其中帶頭結點鏈表第一個節點只有數據域有意義,存的是第一個有效節點的地址
而不帶頭結點鏈表有一個頭指針 即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;會跨過釋放了的空間指向下一個空間
當執行到最後一個的時候就變成
所以要讓變爲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;
}
指導人—微易碼 教主
微易碼官網