人生如棋,我愿为卒,行走虽慢,可有谁见我后退一步?
目录
单链表
链表是线性表的链式存储方式。逻辑上相邻的数据在计算机内的存储位置中不一定相邻。链式存储:通过指针串起来。
单链表的存储方式
链式结构中,除了要存储数据元素外,还要存储它的后继元素(下一个结点)的地址。我们把存储数据元素的信息的域称数据域,存储它的后继元素(下一个结点)位置的域称为指针域。这两部分信息组成数据元素的a[i]的存储映像,称为结点(Node)。因此指针都指向下一个结点,都朝一个方向的,这样的链表称为单链表。单链表的存储方式如下图所示:
对于线性表来说,有头有尾,链表也不意外,为了操作方便,还会给链表增加一个不存放数据的头节点(也可以存放表长等信息,整个链表的存取就必须从头指针开始,我们只要牵住头指针就相当于牵住了整个单链表。注头节点不算真节点,辅助用 那么最后一个结点的位置指向哪里呢?最后一个结点的后继不存在所以我们规定链表的最后一个结点指针为NULL(空)。NULL在链表中往往作为链表结束的判断标志如下图。
单链表的节点结构体定义
#ifndef LIST_H
#define LIST_H
typedef int data_t; //为int取个名字叫data_t
typedef struct node
{
data_t data; //数据域
struct node *next;//指针域,保存下一个结点的地址> 。
}NODE;//用typwdef将结构体等价于类型名NODE
假设p是指向链表第i个元素的指针,则该结点a[i]的数据域我们可以表示为a->data。a[i]的指针域可以表示为p->next,p->next 指向a[i+1]的地址(指针)那么a[i+1]的数据域可以表示为p->next->data.
单链表的基本操作
//1.创建头结点 NODE *create_list(void);
//2.判空, 1为空 int empty_list(NODE *head);
//3.求链表长度 int length_list(NODE *head);
//4.按位置插入结点,需要传三个即头结点、要插入的位置> ,要插入的值 int insrert_listpos(NODE *head, int pos,data_t data);
//5.按位置删除链表,需要传递头结点和删除的位置 int delete_linklistpos(NODE *head,int pos);
//6.安置查找,返回地址.所以我们要遍历整个结点 NODE *find_listdata(NODE *head,data_t data);
//7.安置删除链表结点 int delete_listpos(NODE *head, data_t data);
//8.按位置查找,返回地址 NODE *findlistpos(NODE *head, int pos);
//9.按值修改链表 int update_listdata(NODE *head,data_t old_data,data_t, new_data);
//1o.清空链表,即链表只剩头结点 void clear_list(NODE *head);
//11.销毁链表 void destory_list(NODE **head);
//12.链表的输出 void printf_list(NODE *head);
1.创建头结点
//1.创建头结点
NODE *create_list(void)
{
NODE *head = (NODE*)malloc(sizeof(NODE));//利用malloc函数为头结点申请空间,一个结点包含数据域和指针域。
if(NULL == head)//判断头结点的空间是否申请成功。
{
return NULL;
}
head->data = -1;//头结点的数据初始化,无意义。
head->next = NULL;//头结点的数据初始化,无意义.
return head;
}
2.判空
//2.判空, 1为空
int empty_list(NODE *head)
{
if(NULL != head)//判断头结点是否申请成功
{
return ((head->next==NULL) ? 1 : 0);//如果头结点的指针域是空则返回1,反之返回0;
}
else
return -1;//异常
}
3.求链表长度
//3.求链表长度
int length_list(NODE *head)
{
if(NULL == head)
{
return -1;
}
int len = 0;//定义变量len,累加求长度。
NODE *p = head->next;//定义一个结构体指针p,指向头结点下一个结点(有效结点)
while(p != NULL)//遍历整个链表,直到p==NULL时退出循环
{
len++;
p=p->next;//len自加后,指针p移动到下一个结点。
}
return len;
}
4.按位置插入结点
//4.按位置插入结点,需要传三个即头结点、要插入的位置,要插入的值
int insrert_listpos(NODE *head, int pos,data_t data)
{
//1.判断pos插入位置的合法性0~lend
int len=length_list(head);
if(pos < 0 || pos >len)
{
return -1;
}
//2.准备新的结点
NODE *new = (NODE *)malloc(sizeof(NODE));//为新结点开空间
if(NULL == new)//判断新结点空间是否申请成功,NULL == new 表示申请失败
{
return -1;
}
new->data = data;//把要插入的数值,给new的数据域
new ->next = NULL;//初始化new的指针域
//3.找到pos的前一个位置,pos--完成刚好得到pos的前一个位置。
NODE *p=head;//定义结构体指针p,指向头结点。
while(pos--)
{
p=p->next;
}
//4.将new结点插入到p指针指向的结点之后
new->next = p->next;//new的指针域存放p的下一个结点地址。
p->next = new;//p的指针域存放new的地址
return 0;
}
4.1malloc函数
- malloc需要我们自己计算字节数,并且返回的时候要强转成指定类型的指针
- int *p; p = (int *)malloc(sizeof(int));
- (1)malloc的返回是void*,如果我们写成了:p=malloc(sizeof(int));间接的说明了(将void转化给了int*,这不合理)
- (2)malloc的实参是sizeof(int),用于指明一个整型数据需要的大小,如果我们写成p=(int*)malloc(1),那么可以看出:只是申请了一个一个字节大小的空间。
- (3)malloc只管分配内存,并不能对其进行初始化,所以得到的一片新内存中,其值将是随机的。一般意义上:我们习惯性的将其初始化为NULL,当然也可以使用memset函数。
- malloc函数其实就是在内存中找一片指定大小的空间,然后将这个空间的首地址给一个指针变量,这里的指针变量可以是一个单独的指针,也可以是一个数组的首地址,这要看malloc函数中参数size的具体内容。我们这里malloc分配的内存空间在逻辑上是连续的,而在物理上可以不连续。我们作为程序员,关注的是逻辑上的连续,其他的操作系统会帮着我们处理。
- 用malloc函数申请空间后要记得用free();函数释放掉。如果一直不释放可能会造成内存溢出,内存满了,申请空间会失败。
5.按位置删除链表
//5.按位置删除链表,需要传递头结点和删除的位置
int delete_linklistpos(NODE *head,int pos)
{
//1判空
if(empty_list(head))
{
return -1;
}
//2.判断位置的合法性
//2.判断pos插入位置的合法性0~lend
int len=length_list(head);
if(pos < 0 || pos >len)
{
return -1;
}
//3.找到pos的前一个位置
NODE *p = head;
while (pos--)
{
p=p->next;
}
//4.删除pos位子的结点先创建一个*q保存一下pos位置的结点
NODE *q = p->next;
p->next = q->next;//p的指针域存放q的下一个结点地址。
free(q); //释放掉q
q = NULL; //free掉之后要记得让q=NULL
return 0;
}
6.安置查找,返回地址
//6.安置查找,返回地址.所以我们要遍历整个结点
NODE *find_listdata(NODE *head,data_t data)
{
if(NULL == head)
{
return NULL;
}
NODE *p = head;
while(p->next != NULL)//遍历整个链表,从有效结点开始遍历
{
if(p->next->data == data)//如果在链表中找到想查找的数值,则返回该地址。
{
return p->next;
}
else
{
p=p->next;
}
}
return NULL;
}
7.安置删除链表结点
//7.安置删除链表结点
int delete_listpos(NODE *head, data_t data)
{
//1判空
if(empty_list(head))
{
return -1;
}
//2.遍历结点找到里面和data一样的数值
NODE *p = head;
NODE *q=NULL;//用来存放要删除的结点
while(p->next != NULL)
{
if(p->next->data == data)
{
q=p->next;
p->next = q->next;
free(q);
q=NULL;
}
else
{
p=p->next;
}
}
return -1;
}
8.按位置查找,返回地址
//8.按位置查找,返回地址
NODE *findlistpos(NODE *head, int pos)
{
//1.判断pos插入位置的合法性0~lend
int len=length_list(head);
if(pos < 0 || pos >len)
{
return NULL;
}
找到pos的前一个位置
NODE *p = head;
while(pos--)
{
p=p->next;
}
return p->next;
}
9.按值修改链表
//9.按值修改链表
int update_listdata(NODE *head,data_t old_data,data_t new_data)
{
NODE *p = find_listdata(head,old_data);//找到这个数值,返回这个数值的是地址,所以定义结构体指针p。
p->data = new_data;//将p地址的数值修改成new_data.
}
10.清空链表,即链表只剩头结点
//1o.清空链表,即链表只剩头结点
void clear_list(NODE *head)
{
if(NULL == head)
{
return ;
}
NODE *p=head->next;//清空第一个有效结点
NODE *q=NULL;
head->next = NULL;//让头结点的指针域为NULL
while(p != NULL)
{
q=p->next;//q保存下一个地址
free(p);//释放p结点
p=q;
}
return ;
}
11.销毁链表
//11.销毁链表
void destory_list(NODE **head)
{
clear_list(*head);
free(*head);//释放头
*head=NULL;//防止野指针
}
12.链表的输出
//12.链表的输出
void printf_list(NODE *head)
{
if(NULL == head)
{
return;
}
NODE *p=head->next;//让p指向第一个有效结点
while(p != NULL)//遍历整个链表
{
printf("%-3d",p->data);//打印结点的数据
p=p->next;//让p指向下一个结点,当p = NULL时退出循环。
}
printf("\n");
return ;
}
13.man.c 代码
#include <stdio.h>
#include<stdlib.h>
#include "list.h"
int main(int argc, char *argv[])
{
NODE *head = create_list();
if(NULL == head)
{
printf("malloc failed\n");
return -1;
}
int len = length_list(head);
printf("len:%d\n",len);
int i;
printf("请输入整数i,插入1——i的数\n");
scanf("%d",&i);
while(i--)
{
insrert_listpos(head,0,i+1);//调用插入函数,每次从0位置开始插入,直到循环结束,刚好是1到i。
}
printf("插入的数为:\n");
printf_list(head);//调用输出函数
len = length_list(head);
printf("len:%d\n",len);
printf("按值删除表结点,删除 5 \n");
delete_listpos(head, 5);
printf_list(head);//调用输出函数
//5.按位置删除链表,需要传递头结点和删除的位置
printf("按位值删除表结点,删除 6号位置\n");
delete_linklistpos(head,6);
printf_list(head);//调用输出函数
printf("把数值3修改成11\n");
update_listdata(head,3,11);
printf_list(head);//调用输出函数
printf("清空链表,只剩头结点\n");
clear_list(head);//清空链表
len = length_list(head);
printf("len:%d\n",len);
printf("销毁链表\n");
destory_list(&head); //销毁链表
printf("head:%p\n",head);
return 0;
}
结果
- 注:主要用于自己记录学习和方便自己复习,如有错误或不足之处望大家多多指出。