数组作为存放同类数据的集合,给我们在程序设计时带来很多的方便,增加了灵活性。但数组也同样存在一些弊病。如数组的大小在定义时要事先规定,不能在程序中进行调整,这样一来,在程序设计中针对不同问题有时需要3 0个大小的数组,有时需要5 0个数组的大小,
难于统一。我们只能够根据可能的最大需求来定义数组,常常会造成一定存储空间的浪费。
我们希望构造动态的数组,随时可以调整数组的大小,以满足不同问题的需要。链表就是我们需要的动态数组。它是在程序的执行过程中根据需要有数据存储就向系统要求申请存储空间,决不构成对存储区的浪费。
链表是一种复杂的数据结构,其数据之间的相互关系使链表分成三种:单链表、循环链表、双向链表,下面将逐一介绍。
7.4.1 单链表
图7 - 3是单链表的结构。
单链表有一个头节点h e a d,指向链表在内存的首地址。链表中的每一个节点的数据类型为结构体类型,节点有两个成员:整型成员(实际需要保存的数据)和指向下一个结构体类型节点的指针即下一个节点的地址(事实上,此单链表是用于存放整型数据的动态数组)。链表按此结构对各节点的访问需从链表的头找起,后续节点的地址由当前节点给出。无论在表中访问那一个节点,都需要从链表的头开始,顺序向后查找。链表的尾节点由于无后续节点,其指针域为空,写作为N U L L。
图7 - 3还给出这样一层含义,链表中的各节点在内存的存储地址不是连续的,其各节点的地址是在需要时向系统申请分配的,系统根据内存的当前情况,既可以连续分配地址,也可以跳跃式分配地址。
看一下链表节点的数据结构定义:
suct node
{
int num;
suct node *p;
} ;
在链表节点的定义中,除一个整型的成员外,成员p是指向与节点类型完全相同的指针。
在链表节点的数据结构中,非常特殊的一点就是结构体内的指针域的数据类型使用了未定义成功的数据类型。这是在C中唯一规定可以先使用后定义的数据结构。
• 单链表的创建过程有以下几步:
1 ) 定义链表的数据结构。
2 ) 创建一个空表。
3 ) 利用m a l l o c ( )函数向系统申请分配一个节点。
4 ) 将新节点的指针成员赋值为空。若是空表,将新节点连接到表头;若是非空表,将新
节点接到表尾。
5 ) 判断一下是否有后续节点要接入链表,若有转到3 ),否则结束。
• 单链表的输出过程有以下几步
1) 找到表头。
2) 若是非空表,输出节点的值成员,是空表则退出。
3 ) 跟踪链表的增长,即找到下一个节点的地址。
4) 转到2 )。
- #include "stdio.h"
- #include "string.h"
- #include "assert.h"
- #include "stdlib.h"
- #include "windows.h"
- #define COUNT 3
- //定义一个节点结构体
- struct NODE
- {
- unsigned long uID;
- char strName[16]; //用指针的话会出访问冲突异常
- NODE *next;
- };
- //创建一个具有n个节点的链表,从键盘输入数据将其初始化,并返回链表的首节点指针
- NODE *createNode(int n)
- {
- NODE *pHead, //首节点指针
- *pRear, //尾节点指针
- *pNew; //新节点指针
- int i;
- char *strName = new char[16];
- for (i=0; i<n; i++)
- {
- pNew = new NODE;
- do
- {
- printf("请输入ID和名称: ");
- scanf("%d %s",&pNew->uID, strName);
- if (strlen(strName) > 16)
- {
- printf("输入名称长度超出范围,请重新输入: ");
- Sleep(1000);
- }
- }while(strlen(strName)>16);
- strcpy(pNew->strName, strName);
- if (0==i)
- {
- pRear = pHead = pNew;
- }
- else
- {
- pRear->next = pNew;//未节点的下一个地址等于新地址
- }
- pNew->next = NULL;
- pRear = pNew;
- }
- delete []strName;
- return pHead;
- }
- //打印链表中所有节点的数据
- void printNode(NODE *pHead)
- {
- NODE *pTemp = pHead;
- assert(pTemp != NULL);
- while(pTemp != NULL)
- {
- printf("%d %s ",pTemp->uID, pTemp->strName);
- pTemp = pTemp->next;
- }
- }
- //查询链表中具有指定ID的节点,并返回此节点指针
- NODE *searchNode(NODE *pHead, unsigned long uID)
- {
- NODE *pDest = pHead;
- assert(pDest != NULL);
- while (pDest->next!=NULL && pDest->uID!=uID)
- {
- pDest = pDest->next;
- }
- if (pDest->uID == uID)
- {
- return pDest;
- }
- else
- {
- printf("搜索失败,未找到找定ID的节点! ");
- return NULL;
- }
- }
- //删除指定ID的节点
- NODE *deleteNode(NODE *pHead, unsigned long uID)
- {
- NODE *pDest, //要删除的节点
- *pBefore; //前一个节点
- pDest = pHead;
- assert(pDest != NULL);
- while (pDest->next!=NULL && pDest->uID!=uID)
- {
- pBefore = pDest;
- pDest = pDest->next;
- }
- if (pDest->uID == uID)
- {
- if (pDest == pHead)
- {
- pHead = pDest->next;
- }
- else
- {
- pBefore->next = pDest->next;
- }
- free(pDest);
- printf("节点已被删除! ");
- }
- else
- {
- printf("未找到指定节点,无法将其删除! ");
- }
- return pHead;
- }
- //在指定ID的节点后插入一个新的节点
- NODE *insertNode(NODE *pHead, unsigned long uID)
- {
- NODE *pDest, *pNew;
- pDest = pHead;
- assert(pDest != NULL);
- while(pDest->next!=NULL && pDest->uID!=uID)
- {
- pDest = pDest->next;
- }
- if(pDest->uID == uID)
- {
- pNew = new NODE;
- printf("请输入新节点的ID和名称: ");
- scanf("%d %s",&pNew->uID,pNew->strName);
- pNew->next = pDest->next;
- pDest->next = pNew;
- printf("节点插入完成! ");
- }
- else
- {
- printf("未找到指定节点! ");
- }
- return pHead;
- }
- void main()
- {
- NODE *pHead, *pDest;
- int nSelect;
- bool isExit = false;
- unsigned long uID;
- printf("请根据提示输入数据初始化链表: ");
- pHead = createNode(COUNT);
- assert(pHead != NULL);
- printf("链表创建成功!存储数据如下: ");
- printNode(pHead);
- while (!isExit)
- {
- printf("-------------------------- ");
- printf("请选择要进行的操作: ");
- printf("1.打印出链表所有节点数据: ");
- printf("2.输入ID查询名称。 ");
- printf("3.删除指定ID的节点。 ");
- printf("4.在指定ID的节点后插入一个新的节点。 ");
- printf("5.退出。 ");
- printf("--------------------------- ");
- scanf("%d",&nSelect);
- switch(nSelect)
- {
- case 1:
- printf("链表所有节点数据如下: ");
- printNode(pHead);
- break;
- case 2:
- printf("请输入要查询的节点的ID: ");
- scanf("%d",&uID);
- pDest = searchNode(pHead, uID);
- if (pDest != NULL)
- {
- printf("已找到节点,名字为:%s ",pDest->strName);
- }
- break;
- case 3:
- printf("请输入要删除的节点的ID: ");
- scanf("%d",&uID);
- pHead = deleteNode(pHead, uID);
- break;
- case 4:
- printf("请输入要删除的节点的ID: ");
- scanf("%d",&uID);
- pHead = insertNode(pHead, uID);
- break;
- case 5:
- isExit = true;
- printf("程序已退出! ");
- break;
- default:
- printf("按键错误,请重新选择! ");
- break;
- }
- }
- }
自己写的简单单链表程序。包含 创建 插入 查询 删除 显示功能
- # include <iostream.h>
- # include "stdio.h"
- # include "string.h"
- /********定义一个链表结构*****/
- struct link
- {
- int BikeNum;
- link *Next;
- };
- link* Create();//创建一个新节点
- link* Search(int SeaID,link*);
- link* insert(int i,int date,link*);
- link* Delnode(int DelID,link*);
- void Output(link *p);
- void main()
- {
- link* Lhead;
- Lhead=Create();
- Output(Lhead);
- bool quit=false;
- int sel=0;
- while(!quit)
- {
- cout<<"1 插入数据;2 删除数据;3 查询数据;4 显示数据;5 退出;"<<endl;
- cin>>sel;
- int row=0,date=0;
- link* sch=NULL;
- switch(sel)
- {
- case 1://插入
- {
- cout<<"输入要插入的位置"<<endl;
- cin>>row;
- cout<<"输入要插入的数据"<<endl;
- cin>>date;
- Lhead=insert(row,date,Lhead);
- }
- break;
- case 2://删除
- cout<<"输入要删除的号码"<<endl;
- cin>>date;
- Lhead=Delnode(date,Lhead);
- break;
- case 3://查询
- cout<<"输入要查询的数字"<<endl;
- cin>>date;
- sch=Search(date,Lhead);
- if(NULL==sch)
- {
- cout<<"没有相关的数据"<<endl;
- }
- else
- {
- cout<<"查找的结果是:"<<sch->BikeNum<<endl;
- }
- delete sch;
- break;
- case 4://显示
- Output(Lhead);
- break;
- case 5://quit
- quit=true;
- break;
- default:
- break;
- }
- }
- }
- link* Create()
- {
- link *pHead=NULL, //首节点指针
- *pRear=NULL, //尾节点指针
- *pNew=NULL; //新节点指针
- int b=0;
- cout<<"创建链表,填入数据"<<endl;
- for(int a=0;a<3;a++)
- {
- pNew= new link;
- cin>>b;
- pNew->BikeNum=b;
- if(0==a)
- {
- pHead=pRear=pNew;//头指针
- }
- else
- {
- pRear->Next=pNew;//NEXT等于当前的地址
- pNew->Next=NULL;//当前指针的下一个默认是空的。
- }
- pRear=pNew;//尾节点等于当前地址
- }
- cout<<"填写完成"<<endl;
- return pHead;
- }
- void Output(link *p)
- {
- cout<<"显示链表中的数据"<<endl;
- link* node;
- if(p!=NULL)
- {
- node=p;
- do
- {
- cout<<node->BikeNum<<endl;
- node=node->Next;
- }while(node!=NULL);
- }
- else
- {
- cout<<"链表中没有数据"<<endl;
- }
- }
- link* insert(int i,int date,link* p)
- {
- link* head=p;
- link* insert;
- int count=0;
- if(p==NULL)//当没有数据时。插入头链表
- {
- insert=new link;
- insert->BikeNum=date;
- insert->Next=NULL;//连接后面的
- head=insert;
- return head;
- }
- else
- {
- for(int a=0;a<i-1;a++)
- {
- p=p->Next;
- }
- if(0==i)
- {
- insert=new link;
- insert->BikeNum=date;
- insert->Next=p;
- head=insert;
- return head;
- }
- else
- {
- insert=new link;
- insert->BikeNum=date;
- insert->Next=p->Next;
- p->Next=insert;
- return head;
- }
- }
- return head;
- }
- link* Delnode(int DelID,link* p)
- {
- link* del;
- link* head=p;
- while(p!=NULL)
- {
- if(DelID==p->BikeNum)
- {
- if(head==p)
- {
- head=p->Next;
- delete p;//从堆中删除内存
- return head;
- }
- else
- {
- del->Next=p->Next;
- delete p;//从堆中删除内存
- return head;
- }
- }
- del=p;
- p=p->Next;
- }
- cout<<"删除失败,没有找到节点"<<endl;
- return head;
- }
- link* Search(int date,link* p)
- {
- link* re=NULL;
- while(p!=NULL)
- {
- if(p->BikeNum==date)
- {
- re=p;
- return re;
- }
- p=p->Next;
- }
- return re;
- }
在链表的创建过程中,链表的头指针是非常重要的参数。因为对链表的输出和查找都要从链表的头开始,所以链表创建成