数据结构与算法是一切程序的构架与灵魂。
线性结构:线性表(list),栈(Stack),队列(queue),串(String)
线性结构的存储方式:顺序存储与链式存储。
TOP-1线性表
线性表的定义 :即表中第一个数据没有直接前驱元素与最后一个数据无直接后继元素外,其余表中数据均有唯一的前驱元素与后继元素。
线性表的两种存储结构分别是:顺序存储结构和链式存储结构.
顺序存储 | 链式存储 |
---|---|
(1)结点中只有自身的信息域,没有关联信息域.因此,顺序存储结构的存储密度大、存储空间利用率高 | (1)结点除自身的信息域外,还有表示关联信息的指针域.因此,链式存储结构的存储密度小、存储空间利用率低. |
(2)通过计算地址直接访问任何数据元素,即可以随机访问. | (2)在逻辑上相邻的结点在物理上不必相邻,因此,不可以随机存取,只能顺序存取. |
(3)插入和删除操作会引起大量元素的移动. | (3)插入和删除操作方便灵活,不必移动结点只需修改结点中的指针域即 |
[顺序存储 ]
【1】存储结构的定义
【2】基本的运算 1-初始化(InitList),
2-判断是否空表(ListEmpty)
3-查找元素--1按序号查找(GetElem),-2-按内容查找(LocateElem)
4-插入元素(InserList)
5-删除元素(DeleteList)
6-求线性表的长度(ListLength)
7-清空线性表(ClearList)
【1】存储结构的定义
#define ListSize 100
#define int DataType
typedef struct{
DateType list[ListSize]; //数据元素类型
int length; //数据元素个数
}SeqList; //结构体类型名
注意:两个分量运算符,结构体成员运算符(“.”)与指向结构体成员运算符
(“->”),二者有区别。
相同点:其**右操作符**是成员的名称,都是二元操作符。
不同点:点操作符(.)**左边**的操作数是一个“**结果为结构**”的表达式;
箭头操作符(->)**左边**的操作数是一个**指向结构的指针**。
[!]可自定义在**main()**中定义你所用的是结构体成员(SeqList L;)还是指向结构的指针(SeqList *L;)二者可有(*L).对象=L->对象。
【2】基本运算
(1)初始化(InitList),
void InitList(SeqList *L){
L->length=0; //将其表长置为0
}
(2)判断是否空表(ListEmpty)
int ListEmpty(SeqList L){
if(L.length==0) //如果表长为0
return 1; //则返值1
else
return=0;
}
(3)查找元素
3-1 按序号查找(GetElem)
int GetElem(SeqList L, int i, DataType *e){
if(i<L|| i>L.length) //判断是否在表外
return -1;
*e=L.list[i-1]; //将第i个的位置的元素赋值给e
return 1;
}
3-2 按内容查找(LocateElem)
int LocateElem(SeqList L;DataType e){
int i;
for(i=0;i<L.length;i++) //从第一个起到最后一个元素与e进行比较
if(L.list[i]==e)
return i+1; //找到返回其序号
return 0; //否则返0;
}
int IsertList(SeqList *L, int i, DataType e){
int j;
if(i<1|| i>L->length+1){ //判断位置是否在表内
printf("插入位置不合法\n");
return =-1;
}
else if(L->length>=ListSize){ //判断表是否已经满了
printf("顺序表已满,不能插入元素\n");
return 0;
}
else {
for(j=L->length;j>=i;j--) //对要插入位置的第I个位置以后的元素依次后移
L->list[j]=L->list[j-1];
L->list[i-1]=e; //插入元素到第i个位置;
L->length=L-length+1; //表长加1
return 1;
}
}
int DeleteList(Seqlist *L, int i, DataType *e){
int j;
if(L->length<=0){
printf("顺序表已经空了,不能进行删除\n");
return 0;
}
else if(i<1||i>=L->length){
printf("删除位置有误\n")
return 0;
}
else{
*e=L->list[i-1];
for(j=i;j<=L->length-1;j++)
L->list[j-1]=L->list[j]; //将要删除第i个位置元素以后的元素依次向前移动
L->length=L->length-1; //表长减去1
}
}
6-求线性表的长度(ListLength)
int ListLength(SeqList L){
return L.length;
}
7-清空线性表(ClearList)
void ClearList(SeqList *L){
L->length=0;
}
[链式存储 ]
【1】存储结构的定义
【2】基本的运算 1-初始化(InitList),
2-判断是否空表(ListEmpty)
3-查找元素--1按序号查找(*GetElem),-2-按内容查找(*LocateElem)
4-定位操作(Locatepos)
5-插入元素(InsertList)
6-删除元素(DeleteList)
7-求线性表的长度(ListLength)
8-销毁线性表(DestroyList)
【3】循环链表(circular linked list)
【4】双向链表(double linked list)
(1)单链表的定义:采用一组任意的存储单元(可是连续的或不连续),在除去自身信息(data)外,还具有一个指示其直接后继元素的信息(next)即一个节点包含两个域,数据域与指针域。正是通过指针域将线性表中的n个元素按照逻辑顺序链在一起构成了链表。
单链表的逻辑状态
》为了操作方便,我们在第一个节点前增加一个节点,即头节点。可利用其数据域存放表长信息,指针域存放第一个节点的地址信息,使其指向第一个元素地址。
【2】基本运算
(1)存储结构
typedef struct Node{
Datatype data;
struct Node *data;
}ListNode, *LinkList; //LostNode为链表的节点类型, LinkList指向链表节点的指针类型;
(2)1-初始化(InitList),
void InitList(LinkList *Node){
if((*head=(LinkList)malloc(sizeof(ListNode)))==NULL) //为头结点申请一个内存空间
exit(-1);
(*head)->next=NULL; //将链表的指针域置为空
}
2-判断是否空表(ListEmpty)
int ListEmpty(LinkList head){
if(head->next==NULL) //如果头结点的指针域为空
return 1;
else
return 0;
}
(3)-查找元素- -
3-1按序号查找(*GetElem),
ListNode *GetElem(LinkList head, int i){
ListNode *p;
int j;
if(ListEmpty (head)) //判断是否空
return NULL;
if(i<1) //不在范围内
return NULL;
j=0;
p=head;
while((P->next)!=NULL&&j<i){
p=p->next;
j++;
}
if(j==i)
return p;
else
return NULL;
}
3-2-按内容查找(*LocateElem)
ListNode *LcateElem( Linklist head, DataType e){
ListNode *p;
p=head->next; //指针p指向第一个节点
while(p){
if(p->head !=e)
p=p->next; //继续寻找下一个元素;
else
break; //找到就跳出循环;
}
return p;
}
4-定位操作(Locatepos)
int Locatetepos(LinkList head, DataType e){
ListNode *p; //定义一个指针
int i;
if(ListEmpty(head)) //判断是否表空
return 0;
p=head->next;
i=1;
while(p){
if(p->data!=e) //找到与e相等的值返回其序号
return i;
else{
p=p->next;
i++;
}
}
if(!p)
return 0;
}
5-插入元素(InsertList)
在链表中插入,我们就要先把要插入节点的地方打断。
①将上一个元素的直接后继节点变为插入节点的直接后继。
②将上一个元素的直接后继节点与插入节点相连。
算法实现如下
int IsertList (LinkList head, int i, DataType e){
ListNode *pre, *p; //定义第i个元素的前驱节点指针,P为指向插入节点的指针。
int j;
pre=head;
j=0;
while(pre->next !=NULL && j<i-1){ //寻找那个要插入的前驱节点
pre=pre->next;
j++;
}
if(j!=i-1){ //未找到则输出有误
printf("插入位置有误\n");
return 0;
}
if(p=(ListNode *)malloc(sizeof(ListNode)))==NULL) //生成一个新节点再将e值赋值给它
exti(-1);
p->data=e;
p->next=pre->next;
pre->next=p;
return 1;
}
6-删除元素(DeleteList)
》》删除节点:即将要删除的第i个节点的直接前驱节点绕过指向它的直接后继即可;
int DeleteList( LinkList head, int i, DataType *e){
ListNode *pre, *p;
int j;
pre=head;
j=0;
while(pre->next !=NULL &&pre->next->next!=NULL &&j<i-1){ //寻找删除位置
pre=pre->next;
j++;
}
if(j!=i-1){
printf("无有效删除位置\n");
return 0;
}
p=pre->next; //指向单链的第i个节点,并将该节点的数据域值赋值*e;
*e=p->data;
pre->next=p->next;
free(p);
return 1;
}
7-求线性表的长度(ListLength)
int ListLength(LinkList head){
ListNode *p;
int count=0;
p=head;
while(p->next!=NULL){
p=p->next;
count++;
}
return count;
}
8-销毁线性表(DestroyList)
void DestroyList (LinkList head){
ListNode *p,*q;
p=head;
while(p!=NULL){
q=p;
p=p->next;
free(p);
}
}
【3】循环单链表定义:循环单链表是一种首尾相连的单链表。将整个链表的最后一个节点的指针域由空指针改为指向头结点或者第一个节点。
【4】双向链表(double liked list):即链表中的每个节点都有两个指针域,一个指向直接前驱,另一个指向直接后继 。于是双向链表中的节点就有3个域:data域,prior域,和next域。另外双向链表和循环链表结合就构成力双向循环链表(double circular linked list),
(4-1)存储结构
typedef struct Node {
DataType data;
struct Node *prior;
struct Node *next;
}DListNode, *DLinkList;
(4-2)基础运算
双向链表与单链表的大多数操作类似,而主要在于插入与删除。
1插入操作(在第i个位置插入元素值e的节点)
①寻找第i个节点,用p指向该节点。
②申请一个新节点,由n指向该节点,将e放入数据域。
③修改p和n指向的指针域:另①n->prior=p->prior;与②p->prior->next=n;③n->next=p;④p->prior=n;如下图:
int InsertDList(DListLink head, int i, DataType e){
DListNode *p, *n; //定义节点指针
int j;
p=head->next;
j=0;
while(p!=head&&j<i){ //寻找插入位置
p=p->next;
j++;
}
if(j!=i){
printf("插入位置有误\n");
return 0;
}
n=(DListNode *)malloc(sizeof(DListNode)); //开辟一个内存空间给n节点
if(!n)
return -1;
n->data=e;
n->proir=p->prior; //第一步
p->proir->next=n; //第二步
n->next=p; //第三步
p->prior=n; //第四步
return 1;
}
(2)删除操作-–(删除第i个节点)
①寻找第i个节点,用p指向该节点。
②让p指向的节点与链表断开即①p->prior->next=p->next; - ②p->next->prior=p->prior;
int DeleteDList (DListLink head, int i, DataType *e){
DListNode *p;
int j;
p=head->next;
j=0;
while(p!=head&&j<i){
p=p->next;
j++;
}
if(j!=i){
printf("删除位置有误\n");
return 0;
}
p->prior->next=p->next;
p->next->prior=p->prior;
}
【总结】对于单链表与双链表的整理不难发现后者的灵活性高所占内存空间大,
【参考文献】
【1】《c/c++数据结构算法速学速用大辞典》-----陈锐 等,
【2】《c语言语法详解》
》》如有侵权,联系清删
TOP —HWJ@文健为你持续整理中。。。。