單鏈表的基本操作c語言實現
#include<stdio.h>
#define false 0
#define ok 1
//定義節點的結構
typedef struct node{
int data;
struct node *next;
}node;
typedef struct node *linklist;
//初始化連結串列,建立一個連結串列並賦值
void creatlist(linklist *l,int n)
{
linklist p;
(*l) = (linklist)malloc(sizeof(node));
(*l)->next = NULL;
int i;
for (i = 0; i < n; i++)
{
p= (linklist)malloc(sizeof(node));
p->data = i;
p->next = (*l)->next;
(*l)->next = p;
}
}
//連結串列查詢
int getelem(linklist l, int i, int *e)
{
int k = 1;
linklist p=NULL;
p = l->next;
while (p&&k<i)
{
p = p->next ;
k++;
}
if (!p || k>i)
return false;
*e = p->data;
return ok;
}
//連結串列特定位置插入元素
int listinsert(linklist *l, int i, int e)
{
int k = 1;
linklist s=NULL, p=NULL;
p = *l;
int j = 1;
while (p&&j<i)
{
p = p->next;
j++;
}
if (!p || k>i)
return false;
s = (linklist)malloc(sizeof(node));
s->data = e;
s->next = p->next;
p->next = s;
return ok;
}
//連結串列特定位置刪除元素
int listdelete(linklist *l, int i, int *e)
{
int j = 1;
linklist p=NULL, q=NULL;
p = *l;
while (p&&j<i)
{
p = p->next;
j++;
}
if (!p->next || j>i)
return false;
q = p->next;
p->next = q->next;
*e = q->data;
free(q);
return ok;
}
//列印連結串列
int seelist(linklist l)
{
linklist p=NULL;
p = l->next;
int k = 0;
while (p)
{
printf("%4d", p->data);
p = p->next;
k++;
}
if (k == 0)
{
printf("連結串列為空");
return false;
}
return ok;
}
//刪除整個連結串列
int clearlist(linklist *l)
{
linklist p = NULL,q=NULL;
p = (*l)->next;
while (p)
{
q = p->next;
free(p);
p = q;
}
(*l)->next = NULL;
return ok;
}
int main(void)
{
int a;
linklist lb;
creatlist(&lb, 5);
seelist(lb);
listinsert(&lb, 2, 0);
seelist(lb);
listdelete(&lb, 2, &a);
seelist(lb);
clearlist(&lb);
return 0;
}
1、剛申請指標就要指向NULL;給指標初始化時,如果不是規定它指向某個地址就要malloc申請空間;不用的指標要free。
2、typedef struct node linklist;這語句的含義是以後linklist就代表struct node 。這裡的好處是程式設計更美觀簡潔。畢竟要用到指向指標的指標,不用這個typedef等下就要出現**。
3、以上程式碼頭指標指向了頭結點,這個連結串列擁有頭結點,但是其實這種指向頭結點的頭指標可以不需要指向指標的指標
下面說明為什麼可以不要使用指向指標的指標
頭指標是連結串列必須的元素,linklist p,這裡定義了頭指標,頭指標通常代表該連結串列的名字,如果頭指標p就是頭結點,含有data、next,直接對呼叫p做引數,就可以更改p指向的結構的值,不需要指向指標的指標
先看一段簡單程式碼:
#include<stdio.h>
#include<stdlib.h>
typedef struct node
{
int data;
struct node *next;
}node,*link;
//初始化頭結點,此時頭指標就是頭結點
void init(link p)
{
p->data = 0;
p->next = NULL;
}
//插入元素
void insrt(link p, int i,int a)
{
int j = 1;
link b= (link)malloc(sizeof(node));
link c = p;
while (j < i)
{
c = c->next;
j++;
}
b->data = a;
b->next = c->next;
c->next = b;
}
//顯示連結串列
void seeq(link q)
{
link b = q->next;
while (b != NULL)
{
printf("%d", b->data);
b = b->next;
}
}
void main()
{
int i;
link l1 = (link)malloc(sizeof(node));
init(l1);
for (i= 1; i < 5; i++)
{
insrt(l1, i, i);
}
seeq(l1);
}
對第一段長的程式碼進行改變也無需指向指標的指標,我們把所有的*l改成l,再把主函式裡的&去掉
#include<stdio.h>
#define false 0
#define ok 1
typedef struct node {
int data;
struct node *next;
}node,*linklist;
void creatlist(linklist l, int n)
{
linklist p;
//去掉了l = (linklist)malloc(sizeof(node));
l->next = NULL;
int i;
for (i = 0; i < n; i++)
{
p = (linklist)malloc(sizeof(node));
p->data = i;
p->next = l->next;
l->next = p;
}
}
int getelem(linklist l, int i, int *e)
{
int k = 1;
linklist p = NULL;
p = l->next;
while (p&&k<i)
{
p = p->next;
k++;
}
if (!p || k>i)
return false;
*e = p->data;
return ok;
}
int listinsert(linklist l, int i, int e)
{
int k = 1;
linklist s = NULL, p = NULL;
p = l;
int j = 1;
while (p&&j<i)
{
p = p->next;
j++;
}
if (!p || k>i)
return false;
s = (linklist)malloc(sizeof(node));
s->data = e;
s->next = p->next;
p->next = s;
return ok;
}
int listdelete(linklist l, int i, int *e)
{
int j = 1;
linklist p = NULL, q = NULL;
p = l;
while (p&&j<i)
{
p = p->next;
j++;
}
if (!p->next || j>i)
return false;
q = p->next;
p->next = q->next;
*e = q->data;
free(q);
return ok;
}
int seelist(linklist l)
{
linklist p = NULL;
p = l->next;
int k = 0;
while (p)
{
printf("%4d", p->data);
p = p->next;
k++;
}
if (k == 0)
{
printf("連結串列為空");
return false;
}
return ok;
}
int clearlist(linklist l)
{
linklist p = NULL, q = NULL;
p = l->next;
while (p)
{
q = p->next;
free(p);
p = q;
}
l->next = NULL;
return ok;
}
int main(void)
{
int a;
linklist lb = (linklist)malloc(sizeof(node));//這裡有變動!
creatlist(lb, 5);
seelist(lb);
listinsert(lb, 2, 0);
seelist(lb);
listdelete(lb, 2, &a);
seelist(lb);
clearlist(lb);
return 0;
}
可以看到除了&和,還有兩處變動,就是把原本init函式內的為指標申請空間改到了主函式內一開始申明頭指標時就申請空間,否則會報錯:使用了未初始化的區域性變數lb。這是因為,剛申明指標時,即linklist lb,編譯器會為lb分配空間(32位系統4位元組,64位系統8位元組),此時lb的地址是已知的,但是lb的值時不知道的(malloc時才會定下來lb的值)。如果函式呼叫不知道值的lb,是沒辦法呼叫的。而如果函式是呼叫lb的地址,因為lb的地址是已知的,就可以呼叫了,可以在函式內再為lb申請空間。即函式呼叫的引數一定是初始化值了的引數*
但是實際上,如果頭指標linklist p,p的值就是一個單純的指標,它的值就是第一個結點的地址。按照上面的方法,我們想在連結串列頭插入一個結點b,b的地址為xxx,p->next=地址xxx,此時就改變了第一個結點的next的值,即第一個結點指向地址xxx,即b變成了第二個結點,沒辦法做到讓b變成第一個結點!所以需要改變p的值,讓p指向b,既然改變指標本身的值,而不是指標指向的物件的值,就需要用到指向指標的指標了。