链表是非常重要的数据结构,它是动态地分配内存和动态增删的一种数据结构。与数组比起来
- 数组需要连续的存储空间,而且大小固定(至少c是这样)不够灵活,但是优点就是访问速度快 。 (因为是连续的地址空间,访问第i个元素,逻辑地址 = sizeof(元素类型)*i);
- 链表相对比较灵活,存储空间可以使离散的,大小可以动态扩充,增删等操作,但访问速度比较慢,因为需要整个链表地从头遍历,灵活带来的是维护代价。
c语言实现:
#include <stdio.h>
#include <stdlib.h>
/*功能:*/
/* 1.初始化线性表,输入n个数据 */
/* 2.打印链表,链表的遍历*/
/* 3.清除线性表L中的所有元素,即释放单链表L中所有的结点,使之成为一个空表 */
/* 4.返回单链表的长度 */
/* 5.检查单链表是否为空,若为空则返回1,否则返回0 */
/* 6.返回单链表中第pos个结点中的元素,若pos超出范围,则报错 */
/* 7.向单链表的表头插入一个元素 */
/* 8.向单链表的末尾添加一个元素 */
/* 9.向单链表中第pos个结点位置插入元素为x的结点 */
/* 10.从单链表中删除第pos个结点并返回它的值,若删除失败则报错,并返回原始链表 */
typedef struct node
{
int number;
struct node *next;
}List;
List *init()
{
int n;
List *head=NULL,*p,*p1;
printf("输入一个整数表示链表长度\n");
scanf("%d",&n);
if(n>0)
{
p1=p=(List *)malloc(sizeof(List));
if(p==NULL)
{
printf("内存申请失败!\n");
return NULL;
}
head = p1;
scanf("%d",&p1->number);
p1->next = NULL;
n=n-1;
while(n>0)
{
n--;
p=(List *)malloc(sizeof(List));
if(p==NULL)
{
printf("内存申请失败!\n");
return NULL;
}
scanf("%d",&p->number);
p1->next = p;
p1 = p;
}
p1->next = NULL;
}
return head;
}
//打印链表
void printList(List *phead)
{
if(phead == NULL)
{
printf("该链表为空!\n");
}else
{
while(phead!=NULL)
{
printf("%d ",phead->number);
phead = phead->next;
}
printf("打印完成!\n");
}
}
List * clearList(List *head)
{
List *pt;
while(head!=NULL)
{
pt = head->next;
free(head);
//head = NULL;
head = pt;
}
printf("clear task funish!\n");
return head;
}
int lengthList(List *head)
{
int res=0;
if(head==NULL)
{
res=0 ;
}else
{
while(head!=NULL)
{
res++;
head = head->next;
}
}
return res;
}
int isEmptyList(List *head)
{
if(head==NULL)
return 1;
else
return 0;
}
int getValueList(List *head,int n)
{
int res=0;
if(n>lengthList(head))
{
printf("超出链表长度!\n");
}else
{
while(n>0 && head!=NULL)
{
n--;
res = head->number;
head = head->next;
}
}
return res;
}
List* insertHead(List *head,int value)
{
List *p;
p=(List *)malloc(sizeof(List));
if(p==NULL) printf("申请内存失败!\n");
else
{
p->number=value;
p->next = head;
head = p;
}
return head;
}
List *insertTail(List *head,int v)
{
List *p,*pt;
pt=head;
p=(List *)malloc(sizeof(List));
if(p==NULL) return head;
p->number = v;
p->next=NULL;
if(head==NULL)
{
head = p;
}else
{
while(head->next!=NULL)
{
head=head->next;
}
head->next = p;
}
return pt;
}
List* insertList(List *head,int pos,int value)
{
List *p,*pt,*p1;
p1 = head;
if(pos>lengthList(head)+1)
{
printf("超出链表范围!\n");
return head;
}else
{
p=(List*)malloc(sizeof(List));
p->number = value;
p->next = NULL;
if(head==NULL && pos == 1)
{
p1=p;
}else if(head != NULL && pos ==1)
{
p->next=head;
p1=p;
}else
{
while(pos>2)
{
pos--;
head=head->next;
}
pt = head->next;
head->next = p;
p->next = pt;
}
}
return p1;
}
List* deleteList(List *head,int pos)
{
List *p,*p1,*p2;
p2 = head;
if(pos>lengthList(head) || pos<=0)
{
printf("删除位置不可用!\n");
return head;
}else
{
if(pos == 1) //第一位 特殊处理
{
p=head->next;
free(head);
head=p;
}else
{
while(pos>1)
{
pos--;
p1 = p2; //保留前一结点
p2 = p2->next;
}
p1->next = p2->next; //跳过p2结点
free(p2); //释放内存
}
}
return head;
}
int main()
{
//int length;
List *Head=NULL;
Head = init(); //初始化链表
printList(Head); //打印链表
//Head = clearList(Head);
//length = lengthList(Head);
//printf("%d",getValueList(Head,2));
//Head = insertHead(Head,4);
//Head = insertTail(Head,100);
//Head = insertList(Head,2,50);
Head = deleteList(Head,1);
printList(Head);
return 0;
}
小结:链表用c的结构体和指针实现比较容易。我在实现的清空链表的实现,遇到问题,因为Head->next 有时不为NULL,所以在遍历的时候瞬间爆炸,永远遍历不完0.0。
经过的我分析 原来是free()函数的问题。书上讲这个函数比较粗略。
我看了源码才明白:
void free(void *ptr)
{
struct mem_control_block *free;
free = ptr - sizeof(struct mem_control_block);
free->is_available = 1;
return;
}
毕竟编译器不能执行释放内存这种事,他是操作系统该做的。所以呢,它只能告诉操作系统,这块内存地址又可以重新使用了。(free->is_available = 1;) 那操作系统用不用是它的事了,所以我们在使用free的时候要多加小心了。看下面的代码:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p;
int i=2333;
p=(int*)malloc(sizeof(int));
p=&i;
printf("%d\n",*p);
free(p);
printf("%d",*p);
return 0;
}
这段代码有80%的可能输出两个2333,但是还有可能致使程序崩溃0.0。
原因就是有20%的可能操作系统把这个地址用了不知道保存了什么东东。
《完》