数据结构:单链表基本操作
单链表学习:参考数据结构:单链表基本操作
涉及到单链表的基本操作有如下:
int initList(linkList *); //初始化一个单链表,具有头指针,头结点,头结点->next=NULL;
int createListHead(linkList *, int n); //头插法创建一个链表,链表长度为n;
int createListTail(linkList *, int n); //尾插法创建一个链表,链表长度为n;
int getlength(linkList *); //获取单链表的长度;
int printList(linkList *); //打印整个链表;
int getElem(linkList *,int i,ElemType *); //获取链表中第i个位置处节点的数据元素;
int insertList(linkList *, int i, ElemType data); //在链表的指定位置(i节点)插入一个节点,i的范围为1~length(链表总长度);
int insertListTail(linkList *, ElemType data); //给链表追加一个节点,在最末尾处增加;
int deleteList(linkList *, int i, ElemType *data); //删除指定位置(i节点)处的节点,i的范围为1~length(链表总长度);
int clearList(linkList *); //删除整个链表,使头结点->next=NULL;
(一)ADT:
typedef int ElemType;
typedef struct Node {
ElemType data;
struct Node * next;
}Node;
typedef struct Node* linkList;
(二)初始化:int initList(linkList *L)
int initList(linkList *L)
{
(*L) = (linkList)malloc(sizeof(Node));
(*L)->next = NULL;
printf("链表初始化成功\n");
return 1;
}
初始化一个指向头节点的指针,使头指针->next=NULL,头指针->data不做处理;
(三)创建一个单链表
int createListHead(linkList *L,int n) {
linkList p;
int i = 0;
srand((int)time(0));
for (i = 0; i < n; i++)
{
p= (linkList)malloc(sizeof(Node));
p->data = rand() % 100;
printf("testing:Node[%d]=%d\n",i+1,p->data);
p->next = (*L)->next;
(*L)->next = p;
}
printf("链表(头插法)创建成功\n");
return 1;
}
说明:
1、图中的head表示头指针,而在创建单链表中的入参phead是指向指针的指针;即*phead等同于head,*head指向头节点;
2、初始化和创建整表的函数中,为什么入参是linkList *类型的参数呢,为什么不是Node *类型的;之前我的理解是可以使用Node*类型的入参,这样声明一个Node *head,这样在创建整表的函数中可以直接createList(head,10),直接传入头指针,但是尝试过以后,会在通过头指针访问各节点数据的时候,弹出错误窗口,原因还不太清楚;
3(补充)在看《C和指针》258的时候,看到了这么一句话,好像可以解释为什么需要传入链表的基本操作函数中的入参是一个指针类型的变量;
“i='a';*pi='a';**ppi='a'; //这三条语句具有同样的效果;i-整型变量,pi-指向i的指针,ppi-指向pi指针的指针;
在一条简单的对i赋值的语句就可以完成任务的情况下,为什么还要使用更为复杂的涉及间接访问的方法呢?这是因为简单赋值并不总是可行,例如链表的插入。在那些函数中,我们无法使用简单赋值,因为变量名在函数的作用域内部是未知的。函数所拥有的只是一个指向需要修改的内存位置的指针,所以要对该指针进行间接访问操作以访问需要修改的变量。
”
int createListTail(linkList *L, int n) {
linkList p, temp;
temp = (*L);
int i;
srand((int)time(0));
for (i = 0; i < n;i++) {
p = (linkList)malloc(sizeof(Node));
p->data = rand() % 100;
printf("testing:Node[%d]=%d\n", i + 1, p->data);
p->next = NULL;
temp->next = p;
temp = p;
}
printf("链表(尾插法)创建成功\n");
return 1;
}
(四)获取链表的长度
int getlength(linkList *L) {
linkList p;
int length=0;
p = (*L)->next;//p指向第一个节点;
while (p) {
length++;
p = p->next;
}
return length;
}
(五)打印整个链表
int printList(linkList *L) {
linkList p;
int i = 0;
p = (*L)->next;//p指向第一个节点;
printf("-----------打印整个链表-----------\n");
if (p==NULL) {
printf("这是一个空链表.\n");
}
while (p) {
i++;
printf("第%d个节点的数据data为=%d\n",i,p->data);
p = p->next;
}
return 1;
}
(六)获取指定位置处的节点元素;
int getElem(linkList *L, int i, ElemType *getdata) {
linkList p;
p = (*L)->next;
if (p == NULL)
{
printf("链表为空,请创建一个链表\n");
*getdata = -1;
return 0;
}
if (i < 1)
{
printf("您所查询的节点%d,应该大于0,请重新输入查询\n",i);
*getdata = -1;
return 0;
}
int j = 1;
while (p&&j<i) {
j++;
p = p->next;
}
if (p == NULL)
{
printf("您所查询的节点%d,已经超出了数组的长度\n",i);
*getdata = -1;
return 0;
}
*getdata = p->data;
printf("查询成功!\n", i);
return 1;
}
(七)插入节点;
插入节点分为两种,一种是在链表的长度范围内插入节点,第二种是在链表的尾部追加一个节点;
假设链表的长度为10,第一种可以在1-10位置处插入节点,比如在第10个位置插入一个节点,则原先的第10节点变为了11节点,但是第二种是所有节点位置都不变,在第11个位置追加一个新的节点;
int insertList(linkList *L, int i, ElemType data)
{
linkList p;
linkList insNode;
p = (*L);
int j=0;
// 链表为空,在第1个位置插入一个新的节点;
if (p ->next == NULL) {
printf("链表为空,默认在第一个位置插入一个节点.\n");
insNode = (linkList)malloc(sizeof(Node));
insNode->data = data;
insNode->next = p->next;
p->next = insNode;
printf("节点插入成功.\n");
return 1;
}
// 链表非空的情况下,可以在i=1~length的位置插入节点,如果超过了链表的长度,就会提示错误;
// 其实如果在length+1的位置处插入一个新节点,就相当于在尾部追加一个节点,在本函数中会报错,可以单独实现一个函数;
while(p && j<i-1)
{
j++;
p = p->next;
//printf("j=%d\tp->data=%d\n", j, p->data);
}
if (p->next==NULL) {
printf("您要插入的位置,超过了链表的长度 %d,请重新操作!\n",j);
return 0;
}
insNode = (linkList)malloc(sizeof(Node));
insNode->data = data;
insNode->next = p->next;
p->next = insNode;
printf("节点插入成功\n");
return 1;
}
追加节点;
int insertListTail(linkList *L, ElemType data)
{
linkList temp;
linkList p=(*L);
while(p) {
temp = p;
p = p->next;
}
p = (linkList)malloc(sizeof(Node));
p->data = data;
p->next = NULL;
temp->next = p;
printf("节点插入成功\n");
return 1;
}
(八)删除节点;
int deleteList(linkList *L, int i, ElemType *data)
{
linkList p,pnext;
int j = 0;
p = (*L);
if (p->next == NULL) {
printf("链表为空,无法删除指定节点.\n");
*data = -1;
return 0;
}
while (p->next && j<i-1) {
j++;
p = p->next;
//printf("j=%d\t p->data=%d\n",j,p->data);
}//条件最多定位到最后一个节点;
if ( p->next == NULL) {
printf("您要删除的节点,超过了链表的长度 %d,请重新操作!\n", j);
*data = -1;
return 0;
}
pnext = p->next;
p->next = pnext->next;
*data = pnext->data;
free(pnext);
printf("节点删除成功\n");
return 1;
}
(九)删除这个链表;
int clearList(linkList *L) {
linkList p, temp;
p = (*L)->next;//p指向第一个节点
while (p) {
temp = p;
p = p->next;
free(temp);
}
(*L)->next = NULL;
printf("整个链表已经clear.\n");
return 1;
}