第四章 链表
目录标题
4.1指针
指针最重要的两个运算符:
1)& 取地址运算符
2)* 复引用(或间址)运算符
4.1.1指针的危险性
当用c语言进行程序设计时,把所有不指向实际对象的指针设为NULL是一种明智的做法。
另一个明智的程序设计技巧是在进行指针类型之间相互转换时,显式地使用强制类型转换。
4.1.2动态存储分配
堆:在程序执行时进行存储分配。
例子:指针所指空间的分配和回收
int i,*pi;
float f,*pf;
pi=(int *)malloc(sizeof(int));
pf=(float *)malloc(sizeof(float));
*pi=1024;
*pf=3.14;
printf("an integer=%d,a float=%f\n",*pi,*pf);
free(pi);
free(pf);
malloc的返回结果是 void *;
当检查使用指针和动态存储分配的程序时,一定要保证,在不再使用动态存储区域时,将它归还给系统。
4.2单向链表
需要下列机制实现链式存储表示:
(1)一种定义结点结构的机制,结点结构含有所有的域。
(2)一种在需要时创建新结点的方法。
(3)一种删除不再需要的结点的方法。
例子:建立二结点的表
list_pointer create2()
{
/*建立二结点的表*/
list_pointer first,second;
first=(list_pointer)malloc(sizeof(list_node));
second=(list_pointer)malloc(sizeof(list_node));
second->link=NULL;
second->data=20;
first->data=10;
first->link=second;
return first;
}
例子:在表的前端插入
void insert(list_pointer *ptr,list_pointer node)
{
/*插入一个新结点的数据=50进入链表的头*/
list_pointer temp;
temp=(list_pointer)malloc(sizeof(list_node));
if(IS_FULL(temp)){
fprintf(stderr,"The memory is full\n");
exit(1);
}
temp->data=50;
if(*ptr){
temp->link=node->link;
node->link=temp;
}
else{
temp->link=NULL;
*ptr=temp;
}
}
例子:表的删除操作
void delete(list_pointer *ptr,list_pointer trail,list_pointer node)
{
/*表的删除,trail是前面结点 ptr 也是头结点*/
if(trail)
trail->link=node->link;
else
*ptr=(*ptr)->link;
free(node);
}
例子:表的输出
void print_list(list_pointer ptr)
{
printf("The list contains: ");
for(;ptr;ptr=ptr->link)
printf("%4d",ptr->data);
printf("\n");
}
4.3动态链栈与动态链队列
例子:栈链的插入
void add(stack_pointer *top, element item)
{
/*添加一个数据到栈的头部*/
stack_pointer temp=(stack_pointer)malloc(sizeof(stack));
if(IS_FULL(temp)){
fprintf(stderr,"The memory is full\n");
exit(1);
}
temp->item=item;
temp->link=*top;
*top=temp;
}
例子:链栈的删除
element delete(stack_pointer *top){
/*链栈的删除*/
stack_pointer temp=*top;
element item;
if(IS_EMPTY(temp)){
fprintf(stderr,"The stack is empty\n");
exit(1);
}
item=temp->item;
*top=temp->link;
free(temp);
return item;
}
例子:在链式队列的队尾的插入
void addq(queue_pointer *front,queue_pointer *rear,element item)
{
/*在队列的尾部插入元素*/
queue_pointer temp=(queue_pointer)malloc(sizeof(queue));
if(IS_FULL(temp)){
fprintf(stderr,"The memory is full\n");
exit(1);
}
temp->item=item;
temp->link=NULL;
if(*front) (*rear)->link=temp;
else *front=temp;
*rear=temp;
}
例子:从链式队列的前端删除
element deleteq(queue_pointer *front)
{
/*删除一个队列的元素*/
queue_pointer temp=*front;
element item;
if(IS_EMPTY(*front)){
fprintf(stderr,"The queue is empty\n");
exit(1);
}
item=temp->item;
*front=temp->link;
free(temp);
return item;
}
4.4多项式
4.4.1多项式的单向链表表示
4.4.2多项式加法
例子:两个多项式相加
poly_pointer padd(poly_pointer a,poly_pointer b)
{
/*返回一个a加b的多项式*/
poly_pointer front,rear,temp;
int sum;
rear=(poly_pointer)malloc(sizeof(poly_node));
if(IS_FULL(rear)){
fprintf(stderr,"The memory is full\n");
exit(1);
}
front=rear;
while(a&&b)
switch(COMPARE(a->expon,b->expon)){
case -1: /*a->expon<b->expon*/
attach(b->coef,b->expon,&rear);
b=b->link;
break;
case 0:/*a->expon=b->expon*/
sum=a->coef+b->coef;
if(sum) attach(sum,a->expon,&rear);
a=a->link; b=b->link; break;
case 1:/*a->expon>b->expon*/
attach(a->coef,a->expon,&rear);
a=a->link;
}
/*复制剩下的链表a和链表b*/
for(; a;a=a->link) attach(a->coef,a->expon,&rear);
for(; b;b=b->link) attach(b->coef,b->expon,&rear);
rea->link=NULL;
/*删除其他的初始化结点*/
temp=front; front=front->link; free(temp);
return front;
}
例子:向表的尾端加入结点
void attach(float coefficient,int exponent,poly_pointer *ptr)
{
/*创造一个新结点在 coef=coefficient and expon=exponent,添加它到ptr的头结点。ptr是更新点到这个新的结点*/
poly_pointer temp;
temp=(poly_pointer)malloc(sizeof(poly_node));
if(IS_FULL(temp)){
fprintf(stderr,"The memory is full\n");
exit(1);
}
temp->coef=coefficient;
temp->expon=exponent;
(*ptr)->link=temp;
*ptr=temp;
}
4.4.3多项式删除
链表非常适用于多项式操作。
例子:多项式删除
void erase(poly_pointer *ptr)
{
/*删除多项式的点*/
poly_pointer temp;
while(*ptr){
temp=*ptr;
*ptr=(*ptr)->link;
free(temp);
}
}
4.4.4多项式的循环链表表示
如果修改表结构,使最后一个结点的链域指向表的首结点,那么,就可以更有效地释放多项式的所有结点。我们把它称为循环链表。最后结点的链域为空的单向链表称为链。
例子:函数get_node
poly_pointer get_node(void)
/*提供一个结点去使用*/
{
poly_pointer node;
if(avail){
node=avail;
avail=avail->link;
}
else{
node=(poly_pointer)malloc(sizeof(poly_node));
if(IS_FULL(node)){
fprintf(stderr,"The memory is full\n");
exit(1);
}
}
return node;
}
例子:函数ret_node
void ret_node(poly_pointer ptr)
{
/*返回一个结点到可利用的链表*/
ptr->link=avail;
avail=ptr;
}
例子:删除循环链表
void cerase(poly_pointer *ptr)
{
/*删除循环链表*/
poly_pointer temp;
if(*ptr){
temp=(*ptr)->link;
(*ptr)->link=avail;
avail=temp;
*ptr=temp;
}
}
例子:循环存储表示的多项式加法
poly_pointer cpadd(poly_pointer a,poly_pointer b)
{
/*多项式a和b是单独链接循环列表和头结点一起。返回a的多项式,它是a和b的和*/
poly_pointer starta,d,lastd;
int sum,done=FALSE;
starta=a; /*报告a的开始*/
a=a->link; /*跳过头节点给a和b*/
b=b->link;
d=get_node(); /*给和得到一个头结点*/
d->expon=-1; lastd=d;
do{
switch(COMPARE(a->expon,b->expon)){
case -1:/*a->expon<b->expon*/
attach(b->coef,b->expon,&lastd);
b=b->link;
break;
case 0:/*a->expon=b->expon*/
if(starta==a) done=TRUE;
else{
sum=a->coef+b->coef;
if(sum) attach(sum,a->expon,&lastd);
a=a->link; b=b->link;
}
break;
case 1:/*a->expon>b->expon*/
attach(a->coef,a->expon,&lastd);
a=a->link;
}
}while(!done)
lastd->link=d;
return d;
}
4.4.5小结
4.5链表的其他操作
4.5.1单向链表的操作
例子:翻转单链表
list_pointer invert(list_pointer lead)
{
/*翻转单链表*/
list_pointer middle,trail;
middle=NULL;
while(lead){
trail=middle;
middle=lead;
lead=lead->link;
middle->link=trail;
}
return middle;
}
例子:串接单链表
list_pointer concatenate(list_pointer ptr1,list_pointer ptr2)
{
/*生成一个新链表包含链表1和链表2.链表暂时放在链表1*/
list_pointer temp;
if(IS_EMPTY(ptr1)) return ptr2;
else{
if(!IS_EMPTY(ptr2)){
for(temp=ptr1;temp->link;temp=temp->link)
;
temp->link=ptr2;
}
return ptr1;
}
}
4.5.2循环链表的操作
例子:在表前端插入结点
void insert_front(list_pointer *ptr,list_pointer nodde)
/*插入结点到表ptr前,并且表ptr是表的最后一个结点*/
{
if(IS_EMPTY(*ptr)){
/*表是空,改变ptr到点的入口*/
*ptr=node;
node->link=node;
}
else{
/*表不是空的,新入口在前面*/
node->link=(*ptr)->link;
(*ptr)->link=node;
}
}
例子:求循环链表的长度
int length(list_pointer ptr)
{
/*发现循环链表ptr的长度*/
list_pointer temp;
int count=0;
if(ptr){
temp=ptr;
do{
count++;
temp=temp->link;
}while(temp!=ptr);
}
return count;
}
4.6等价关系
定义:集合S上的关系=,称为S上等价关系,当且仅当它在S上是对称的,自反的,传递的。
例子:等价算法的第一次求精
void equivalence()
{
initialize;
while(there are more pairs){
read the next pair<i,j>;
process this pair;
}
initialize the output;
do
output a new equivalence class;
while(not done);
}
例子:较详细的等价算法
void equivalence()
{
initialize seq to NULL and out to TRUE;
while(there are more pairs){
read the next pair,<i,j>;
put j on the seq[i] list;
put i on the seq[j] list;
}
for(i=0;i<n;i++)
if(out[i]){
out[i]=FALSE;
output this equivalence class;
}
}
例子:寻找等价类的程序
#include<stdio.h>
#include<alloc.h>
#define MAX_SIZE 24
#define IS_FULL(ptr) (!(ptr))
#define FALSE 0
#define TRUE 1
typedef struct node *node_pointer;
typedef struct node{
int data;
node_pointer link;
}
void main(void)
{
short int out[MAX_SIZE];
node_pointer seq[MAX_SIZE];
node_pointer x,y,top;
int i,j,n;
printf("Enter the size(<=%d)",MAX_SIZE);
scanf("%d",&n);
for(i=0;i<n;i++){
/*初始化seq和out*/
out[i]=TRUE; seq[i]=NULL;
}
/*阶段 1:输入等价对: */
printf("Enter a pair of numbers (-1 -1 to quit): ");
scanf("%d%d",&i,&j);
while(i>=0){
x=(node_pointer)malloc(sizeof(node));
if(IS_FULL(x)){
fprintf(stderr,"The memory is full\n");
exit(1);
}
x->data=j; x->link=seq[i]; seq[i]=x;
x=(node_pointer)malloc(sizeof(node));
if(IS_FULL(x)){
fprintf(stderr,"The memory is full\n");
exit(1);
}
x->data=i; x->link=seq[j]; seq[j]=x;
printf("Enter a pair of numbers (-1 -1 to quit): ");
scanf("%d%d",&i,&j);
}
/*阶段 2:输出等价类*/
for(i=0;i<n;i++)
if(out[i]){
printf("\nNew class: %5d",i);
out[i]=FALSE; /*将类设置为假*/
x=seq[i]; top=NULL; /*初始化栈*/
for(;;){ /*查找其他类内容*/
while(x){ /*进程列表*/
j=x->data;
if(out[j]){
printf("%5d",j); out[j]=FALSE;
y=x->link; x->link=top; top=x; x=y;
}
else x=x->link;
}
if(!top) break;
x=seq[top->data]; top=top->link; /*无栈*/
}
}
}
4.7稀疏矩阵
例子:读取稀疏矩阵
matrix_pointer mread(void)
{
/*在矩阵中读取并设置其链接表示.使用辅助全局阵列 hdnode*/
int num_rows,num_cols,num_terms,num_heads,i;
int row,col,value,current_row;
matrix_pointer temp,last,node;
printf("Enter the number of rows,columns and number of nonzero terms: ");
scanf("%d%d%d",&num_rows,&num_cols,&num_terms);
num_heads=(num_cols>num_rows)?num_cols:num_rows;
/*为头节点列表设置头节点*/
node=new_node(); node->tag=entry;
node->u.entry.row=num_rows;
node->u.entry.col=num_cols;
if(!num_heads) node->right=node;
else { /*初始化头结点*/
for(i=0;i<num_heads;i++){
temp=new_node;
hdnode[i]=temp; hdnode[i]->tag=head;
hdnode[i]->right=temp; hdnode[i]->u.next=temp;
}
current_row=0;
last=hdnode[0]; /*当前行中的最后一个节点*/
for(i=0i<num_terms;i++){
printf("Enter row,colums and value: ");
scanf("%d%d%d",&row,&col,&value);
if(row>current_row){ /*关闭当前行*/
last->right=hdnode[current_row];
current_row=row; last=hdnode[row];
}
temp=new_node();
temp->tag=entry; temp->u.entry.row=row;
temp->u.entry.col=col;
temp->u.entry.value=value;
last->right=temp; /*链接到行列表*/
last=temp;
/*链接到列列表*/
hdnode[col]->u.next->down=temp;
hdnode[col]->u.next=temp;
}
/*关闭最后一行*/
last->right=hdnode[current_row];
/*关闭所有列列表*/
for(i=0;i<num_cols;i++)
hdnode[i]->u.next->down=hdnode[i];
/*将所有头节点链接在一起*/
for(i=0;i<num_heads-1;i++)
hdnode[i]->u.next=hdnode[i+1];
hdnode[num_heads-1]->u.next=node;
node->right=hdnode[0];
}
return node;
}
例子:获得矩阵的新结点
matrix_pointer new_node(void)
{
matrix_pointer temp;
temp=(matrix_pointer)malloc(sizeof(matrix_node));
if(IS_FULL(temp)){
fprintf(stderr,"The memory is full\n");
exit(1);
}
return temp;
}
例子:输出稀疏矩阵
void mwrite(matrix_pointer node)
{
/*以行主要形式打印出矩阵*/
int i;
matrix_pointer temp,head=node->right;
/*矩阵尺寸*/
printf(" \n num_rows=%d,num_cols=%d \n",node->u.entry.row,node->u.entry.col);
printf("The matrix by row,column,and value: \n\n");
for(i=0;i<node->u.entry.row;i++){
/*打印出每行中的条目*/
for(temp=head->right;temp!=head;temp=temp->right)
printf("%5d%5d%5d \n",temp->u.entry.row,temp->->u.entry.col,temp->u.entry.value);
head=head->u.next; /*下一行*/
}
}
例子:删除稀疏矩阵
void merase(matrix_pointer *node)
{
/*删除矩阵,将节点返回到堆*/
matrix_pointer x,y, head=(*node)->right;
int i,num_heads;
/*按行释放入口节点和头节点*/
for(i=0;i<(*node)->u.entry.row;i++){
y=head->right;
while(y!=head){
x=y; y=y->right; free(x);
}
x=head; head=head->u.next; free(x);
}
/*释放剩余的头节点*/
y=head;
while(y!=*node){
x=y; y=y->u.next; free(x);
}
free(*node); *node=NULL;
}
4.8双向链表
例子:双向循环链表的插入操作
void dinsert(node_pointer node,node_pointer newnode)
{
/*在节点右侧插入新节点*/
newnode->1link=node;
newnode->rlink=node->rlink;
node->rlink->1link=newnode;
node->rlink=newnode;
}
例子:双向循环链表的删除操作
void ddelete(node_pointer node,node_pointer deleted)
{
/*从双链表中删除*/
if(node==deleted)
printf("Deletion of head node not permitted.\n");
else{
deleted->1link->rlink=deleted->rlink;
deleted->rlink->1link=deleted->1link;
free(deleted);
}
}