链表组成
把链表看成是由一个个带数据+指向下一个节点地址指针的数据结构所组成的一串数据,这个中间的数据结构可以称为节点
链接主要要素
链表开始地址+节点+结束标志
开始地址->第一个节点的地址
结束标志(NULL)->最后一个节点的指针值
链表创建
例:创建一个链表保存数组,往数组加入数字,知道遇到-1结束输入
1、定义节点:由一个整型数值和一个自己类型的指针组成。
value用来存储数组数值
next用来存储下一个节点的开始地址或者结束标志
#cat node.h
#ifndef _NODE_H_
#define _NODE_H_
typedef struct _node {
int value;
struct _node *next;
} Node;
#endif
2、链表应用
#include "node.h"
#include <stdio.h>
#include <stdlib.h>
/*
typedef struct _node {
int value;
struct _node *next;
} Node;
*/
int main(int argc,char const *argv[])
{
Node * head = NULL; //开始地址直接保存结束符,表示创建空的linked-list
int num;
do{
scanf("%d",&num);
if ( num !=-1 ){
// add to linked-list
Node *p = (Node*)malloc(sizeof(Node));
p->value = num;
p->next = NULL; //加到链表的最后面
// find the last
Node *last = head;
if ( last ){ //考虑边界条件
while ( last->next ){ //last is not NULL continue running
last = last->next;
}
// attach
last->next = p; //其他num加到链表的最后面
}else{
head = p; //加第一个num
}
}
} while ( num !=-1 );
}
改进版:
优化1:把链表的添加抽离出来成为子函数方便调用
优化2:加入一个专门保存最后一个节点地址的指针变量tail,省去每次都循环查找最后一个节点指针地址的计算
#include "node.h"
#include <stdio.h>
#include <stdlib.h>
/*
typedef struct _node {
int value;
struct _node *next;
} Node;
*/
typedef struct _list{
Node* head;
Node* tail; //用来保存最后一个节点的开始地址,如果只有一个节点则与head相等
} List;
void add_ntl(List *pList,int num);
int main(int argc,char const *argv[])
{
List list;
list.head = list.tail = NULL; //创建一个空的linked-list
int num;
do{
scanf("%d",&num);
if ( num !=-1 ){
add_ntl(&list,num);
}
} while ( num !=-1 );
Node* p;
for (p=list.head;p;p=p->next){
printf("%d\n",p->value);
}
printf("\n");
return 0;
}
void add_ntl(List *pList,int num){
// add to linked-list
Node *p = (Node*)malloc(sizeof(Node));
p->value = num;
p->next = NULL; //加到链表的最后面
if ( pList->head ){
pList->tail->next = p; //二级指针,表示上一个节点的next=p实现链接
pList->tail = p; //将tail节点重置为新节点的开始地址,实现追加
}else{
pList->head = p;
pList->tail = p;
//由于需要修改head指针,
//所以转入head指针的指针就能通过这种方式修改head,
//这种方式不需要返回值
}
}
正常输入运行的结果
上述程序有个bug,并没有规定用户只能输入int型数据,当用户输入字符再输入-1程序无法正常结束。
为什么会出现这种现象,应该如何解决?
链表遍历输出
在上面小程序中加一个print_l函数遍历链表中的数据
#include "node.h"
#include <stdio.h>
#include <stdlib.h>
/*
typedef struct _node {
int value;
struct _node *next;
} Node;
*/
typedef struct _list{
Node* head;
Node* tail; //用来保存最后一个节点的开始地址,如果只有一个节点则与head相等
} List;
void add_ntl(List *pList,int num);
void print_l(List *pList);
int main(int argc,char const *argv[])
{
List list;
list.head = list.tail = NULL; //创建一个空的linked-list
int num;
do{
scanf("%d",&num);
if ( num !=-1 ){
add_ntl(&list,num);
}
} while ( num !=-1 );
print_l(&list);
return 0;
}
void add_ntl(List *pList,int num){
// add to linked-list
Node *p = (Node*)malloc(sizeof(Node));
p->value = num;
p->next = NULL; //加到链表的最后面
if ( pList->head ){
pList->tail->next = p; //二级指针,表示上一个节点的next=p实现链接
pList->tail = p; //将tail节点重置为新节点的开始地址,实现追加
}else{
pList->head = p;
pList->tail = p;
//由于需要修改head指针,
//所以转入head指针的指针就能通过这种方式修改head,
//这种方式不需要返回值
}
}
void print_l(List *pList){
Node* p;
for (p=pList->head;p;p=p->next){
printf("%d\n",p->value);
}
}
运行结果展示
链表中节点查找和删除
代码如下
print_l(&list);
Node* p;
Node* q;
scanf("%d",&num);
int Founded = 0;
for ( q=NULL,p=list.head; p;q=p,p=p->next ){
if ( p->value == num ){
if (q){
q->next = p->next;
//q是p的上一个节点,直接让q链接p的下一个节点实现删除
}else{
list.head = p->next;
}
free(p); //释放p的内存
Founded = 1;
printf("找到了,干掉他,new_list:\n");
print_l(&list);
break; //这里只删除第一个找到的数,当需要删除所有找到的数应该怎么做
}
}
if ( !Founded ){
printf("sorry,没到了\n");
}
return 0;
链表清除
遍历free节点直到遇到NULL
for (p=list.head;p;q = p->next,p=q){
free(p);
}