逻辑结构:线性:首元素没有直接前驱,尾元素没有直接后继,其余元素有且只有一个前驱和后继 存储结构:链式存储 插入:头插可以替换尾插 头节点的作用是让插入的代码统一,不用去判断。
链表的结构:
链表的结构如上图所示,每个节点的内部结构为两个部分,data为节点内保存的数据,next为保存下一个节点的地址。
typedef int linklist_data_t;
typedef struct linklist
{
linklist_data_t data;
struct linklist* next;
}lkl_node,*lkl_pnode;
链表的创建:
lkl_pnode create_linklist()
{
lkl_pnode H=(lkl_pnode)malloc(sizeof(lkl_node));//开辟堆区空间
if(H==NULL)
{
printf("malloc is default\n");
return NULL;
}
H->next=NULL;//防止出现野指针
return H;//创建头节点
}
链表的创建需要在堆区开辟一片空间,这样可以手动开辟手动销毁。
链表的插入:
1、尾插:顾名思义就是从链表的尾部插入一个新节点。第一步,让头节点往后移到要插入新节点位置的前一个位置;第二步,将data存入p中;第三步,将 H 的next指向p,p的next指向空指针(NULL)。
while(H->next)
{
H=H->next;
}
lkl_pnode p=create_linklist();
p->data=data;
H->next=p;
p->next=NULL;
2、头插:顾名思义就是从节点之前插入新节点。第一步,让头节点往后移到要插入新节点位置的前一个位置;第二步,将data存入p中;第三步,将p的next指向p的下一个节点即H的next;
第四步,将H的next指向p。
while(H->next)
{
H=H->next;
}
lkl_pnode p=create_linklist();
p->data=data;
p->next=H->next;
H->next=p;
链表的增加:
当H的next为NULL时头插和尾插一样,所以头插可以替换尾插。
int increase_linklist(lkl_pnode H,int pos,linklist_data_t data)
{
if(pos<0||pos>getlen_linklist(H))//判断插入位置是否合理,pos表示新节点插入的位置
{
printf("pos is default\n");
}
while(pos--)//遍历链表找到要插入新节点位置的前一个位置
{
H=H->next;
}
lkl_pnode new=create_linklist();
new->data=data;
new->next=H->next;
H->next=new;
return 0;
}
获取链表长度:
定义一个整型变量用来计数,然后遍历整个链表,可以得到链表的长度。
int getlen_linklist(lkl_pnode H)
{
int count=0;
while(H->next)
{
H=H->next;
count++;
}
return count;
}
链表的判断是否为空:
当链表长度为0时链表为空。返回值为0表示正常退出,返回值为-1表示异常退出。
int empty_linklist(lkl_pnode H)
{
if(0==getlen_linklist(H))
{
return 0;
}
else
{
return -1;
}
}
链表的打印:
int show_linklist(lkl_pnode H)
{
if(0==empty_linklist(H))
{
printf("H is empty\n");
return -1;
}
int i=0;
while(H->next)
{
H=H->next;
printf("data[%d]=%d ",i,H->data);
i++;
}
puts("\n");
return 0;
}
链表的删除(根据位置删除):
遍历链表找到要删除节点的前一个位置,然后将要删除节点的位置保存下来,存到p中,然后将要删除节点的前一个位置与要删除节点的后一个位置相连,最后释放要删除节点的空间。
int delete_linklist(lkl_pnode H,int pos)
{
if(0==empty_linklist(H))
{
printf("H is empty\n");
return -1;
}
if(pos<0||pos>=getlen_linklist(H))
{
printf("pos is default\n");
}
while(pos--)
{
H=H->next;
}
lkl_pnode p=H->next;
H->next=p->next;
free(p);
p=NULL;
return 0;
}
链表的删除(根据值删除):
这里方法与根据位置删除的方法一样,只不过在找要删除节点的前一个位置时不一样,因为是根据值删除,所以在遍历时条件是当头节点的下一个节点的值不等于data时,如果是头节点的值不等于data,找到的是要删除的节点它自己。
int delete1_linklist(lkl_pnode H,linklist_data_t data)
{
if(0==empty_linklist(H))
{
printf("H is empty\n");
return -1;
}
while(H->next->data!=data)
{
H=H->next;
}
lkl_pnode p=H->next;
H->next=p->next;
free(p);
p=NULL;
return 0;
}
链表的修改(根据位置修改):
方法也是找到即将修改的节点的前一个节点,然后将它的下一个节点的值变为data。
int change_linklist(lkl_pnode H,int pos,linklist_data_t data)
{
if(0==empty_linklist(H))
{
printf("H is empty\n");
return -1;
}
if(pos<0||pos>=getlen_linklist(H))
{
printf("pos is default\n");
}
while(pos--)
{
H=H->next;
}
H->next->data=data;
return 0;
}
链表的修改(根据值修改):
首先找到即将的修改节点,遍历链表,条件应当为H的值不等于旧的data,这样就找到了即将修改的节点,然后将它的值改为新的data。
int change1_linklist(lkl_pnode H,linklist_data_t old_data,linklist_data_t new_data)
{
if(0==empty_linklist(H))
{
printf("H is empty\n");
return -1;
}
while(H->data!=old_data)
{
H=H->next;
}
H->data=new_data;
return 0;
}
链表的查询(根据位置查询):
因为查询后返回值为一个数据,所以函数的数据类型应当为linklist_data_t型;首先还是找到需要查询节点的前一个节点,最后返回它下一个节点的data。
linklist_data_t search_linklist(lkl_pnode H,int pos)
{
if(0==empty_linklist(H))
{
printf("H is empty\n");
return -1;
}
if(pos<0||pos>=getlen_linklist(H))
{
printf("pos is default\n");
}
while(pos--)
{
H=H->next;
}
return H->next->data;
}
链表的查询(根据值查询):
函数返回值为查询到值节点的位置;首先遍历整个链表判断H的值是否与data相等,如果等返回节点的位置,不等则继续遍历。
int search1_linklist(lkl_pnode H,linklist_data_t data)
{
if(0==empty_linklist(H))
{
printf("H is empty\n");
return -1;
}
int i=0;
while(H->next)
{
H=H->next;
if(H->data==data)
{
return i;
}
i++;
}
return 0;
}
链表的逆序:
首先定义p和q来保存头节点的下一个节点位置,然后断开头节点连接,然后让p移动,q则保存p的位置,然后q做头插,这样就成了每个节点做头插,形成逆序。
int reverse_linklist(lkl_pnode H)
{
if(0==empty_linklist(H))
{
printf("H is empty\n");
return -1;
}
lkl_pnode p=H->next;
lkl_pnode q=H->next;
H->next=NULL;
while(p)
{
q=p;
p=p->next;
q->next=H->next;
H->next=q;
}
return 0;
}
链表的排序:
这里和链表的逆序方法雷同,只不过在p遍历的同时遍历头节点的下一个节点,判断头节点下一个节点的值是否大于q的值,如果大于退出循环,然后做头插,如果小于继续遍历p。
int sort_linklist(lkl_pnode H)
{
if(0==empty_linklist(H))
{
printf("H is empty\n");
return -1;
}
lkl_pnode p=H->next;
lkl_pnode q=H->next;
H->next=NULL;
while(p)
{
q=p;
p=p->next;
lkl_pnode t=H;
while(t->next)
{
if(t->next->data>q->data)
{
break;
}
t=t->next;
}
q->next=H->next;
H->next=q;
}
return 0;
}
链表的清空:
只需遍历链表是否为空,一个一个删除,就清空了。
int clean_linklist(lkl_pnode H)
{
if(0==empty_linklist(H))
{
printf("H is empty\n");
return -1;
}
while(empty_linklist(H))
{
delete_linklist(H,0);
}
return 0;
}
链表的销毁:
判断链表是否为空,为空就释放头节点,让头节点等于NULL,防止出现野指针,不为空就先清空,然后释放头节点。
int destroy_linklist(lkl_pnode *H)
{
if(0==empty_linklist(*H))//如果为空就释放头节点
{
free(*H);
*H=NULL;
}
else//不为空就清空后再释放头节点
{
clean_linklist(*H);
free(*H);
*H=NULL;
}
return 0;
}