1、线性表的定义
4、顺序表的操作
5、单链表的操作
6、双链表的操作
线性表是具有相同特性数据元素的一个有限序列。该序列中所含元素的个数叫做线性表的长度,用n(n>0)表示。n可以为0,表示该线性表是一个空表。
2、线性表的逻辑特性只有一个表头元素,只有一个表尾元素,表头元素没有前驱,表尾元素没有后继,除表头和表位元素之外,其他元素只有一个直接前驱,也只有一个直接后继。
3、结构体定义
#define maxSize 100 //这里定义一个整型常量maxSize,值为100
//1、顺序表的结构体定义
typedef struct{
int data[maxSize]; //存放顺序元素的数组(默认是int型,可根据题目要求将int转换成其他的数据类型)
int length; //存放顺序表的长度
}Sqlist; //顺序表类型的定义
//2、单链表结点定义
typedef struct LNode{
int data; //data中存放结点数据域(默认是int型)
struct LNode *next; //指向后继结点的指针
}LNode; //定义单链表结点类型
//3、双链表结点定义
typedef struct DLNode{
int data; //data中存放结点数据域(默认是int型)
struct DLNode *prior; //指向前驱结点的指针
struct DLNode *next; //指向后继结点的指针
}DLNode; //定义双链表结点类型
4、顺序表的操作
//返回第一个比x大的元素的位置
int findElem(Sqlist L, int x)
{
int i;
for (i=0; i<L.length; ++i)
{
if (x < L.data[i]) //对顺序表中的元素从小到大逐个进行判断,看x是否小于当前所扫描到的元素,如果小于则返回当前位置i
{
return i;
}
}
return i; //如果顺序表中不存在比x大的元素,则应将x插入表尾元素,返回i来标记这种情况(i<L.length这一句不成立,而退出for循环后,i正好指示了表尾元素之后的位置,同样也是正确的插入位置)
}
//移动元素、并插入新的元素
void insertElem(Sqlist &L, int x) //L本身要发生变化,因此要使用引用型
{
int p,i;
p = findElem(L,x); //调用函数findElem()来找到插入位置p
for (i=L.length-1; i>=p; --i)
{
L.data[i+1] = L.data[i]; //从右向左,逐个将元素右移一个位置
}
L.data[p] = x; //将x放在插入位置p上
++(L.length); //表内元素多了1个,因此表长自增1
}
//在顺序表中查找第一个值为e的元素算法
int findElem(Sqlist L, int e)
{
int i;
for (i=0; i<L.length; ++i)
{
if (e == L.data[i])
{
return i; //若找到,则返回下标
}
}
return -1; //没找到,返回-1,作为失败标记
}
//在第P个位置插入元素e
int insertElem(Sqlist &L, int p, int e) //L本身要发生改变,所以要有引用型
{
int i;
if (p<0 || p>L.length || L.length==maxSize) //位置错误或者表长已经达到顺序表的最大允许值,此时插入不成功,返回0
{
return 0;
}
for (i=L.length-1; i>=p; --i)
{
L.data[i+1] = L.data[i]; //从后往前,逐个将元素往后移动一个位置
}
L.data[p] = e; //将e放在插入位置P上
++(L.length); //表内元素多了1个,长度自增1
return 1; //插入成功,返回1
}
//删除顺序表L中下标为p(0<=p<=length-1)的元素,成功返回1,否则返回0,并将被删除元素的值赋给e。
int deleteElem(Sqlist &L, int p, int &e)
{
int i;
if (p<0 || p>L.length-1)
{
return 0; //位置不对返回0,代表删除不成功
}
e = L.data[p]; //将被删除元素的值赋给e
for (i=p; i<L.length-1; ++i) //从p位置开始,将其后边的元素逐个前移一个位置
{
L.data[i] = L.data[i+1];
}
--(L.length); //表长减1
return 1; //删除成功,返回1
}
//初始化顺序表的算法
//只需将length设置为0,如下:
void initList(Sqlist &L) //L本身要发生改变,所以用引用型
{
L.length = 0;
}
//求指定位置元素的算法
//用e返回L中p(0<=p<=length-1)位置上的元素,如下:
int getElem(Sqlist L, int p, int &e) //要改变,所以用引用型
{
if (p<0 || p>L.length-1) //p值越界错误,返回0
{
return 0;
}
e = L.data[p];
return 1;
}
5、单链表的操作
//将两个递增的单链表归并成一个递增的单链表
void merge(LNode *A, LNode *B, LNode *&C)
{
LNode *p = A->next; //p来跟踪A的最小值结点
LNode *q = B->next; //q来跟踪B的最小值结点
LNode *r; //r始终指向C的终端节点
C = A; //用A的头结点来做C的头结点
C->next = NULL;
free(B); //B的头结点已经没用,因而释放掉
r = C; //r指向C,因为此时头结点也是终端节点
while (p != NULL && q != NULL) //当p和q都不为空时,选取p与q所指结点中较小者插入C的尾部
{
/**
以下的if else语句中,r始终指向当前链表的终端结点,作为接纳新结点的一个媒介,通过它,新结点被链接入C并且重新指向新的终端结点,以便于接收下一个新结点,这里体现了建立链表的尾插法的思想
*/
if (p->data <= q->data)
{
r-next = p;
p = p->next;
r = r->next;
}
else
{
r->next = q;
q = q->next;
r = r->next;
}
}
r->next = NULL; //此句可去掉
if (p != NULL) //将p及p之后链接到C的尾部
{
r->next = p;
}
if (q != NULL) //将q及q之后链接到C的尾部
{
r->next = q;
}
}
//尾插法建立单链表
void createlistR(LNode *&C, int a[], int n) //要改变的变量用引用型
{
LNode *s,*r; //s用来指向新申请的结点,r始终指向C的终端结点
int i;
C = (LNode*)malloc(sizeof(LNode)); //申请C的头结点空间
C->next = NULL;
r = C; //r指向头结点,因为此时头结点就是终端结点
for (i=0; i<n; ++i) //循环申请n个结点来接收数组a中的元素
{
s = (LNode*)malloc(sizeof(LNode)); //s指向新申请的结点
s->data = a[i]; //用新申请的结点来接收a中的一个元素
r->next = s; //用r来接收新结点
r = r->next; //r指向终端结点,以便于接纳下一个到来的结点
}
r->next = NULL; //数组a中所有的元素都已经装入链表C中,C的终端结点的指针域置为NULL,C建立完成
}
//头插法建立单链表
void createlistF(LNode *&C, int a[], int n)
{
LNode *s;
int i;
C = (LNode*)malloc(sizeof(LNode));
C->next = NULL;
for (i=0; i<n; ++i)
{
s = (LNode*)malloc(sizeof(LNode));
s->data = a[i];
//头插法的关键
s->next = C->next; //s所指新结点的指针域next指向C中的开始结点
C->next = s; //头结点的指针域next指向s结点,使得s成为新的开始结点
}
}
//利用头插法将两个递增的单链表归并成一个递减的单链表
void merge(LNode *A, LNode *B, LNode *&C)
{
LNode *p = A->next;
LNode *q = B->next;
LNode *s;
C = A;
C->next = NULL;
free(B);
while (p != NULL && q != NULL)
{
if (p->data <= q->data)
{
s = p;
p = p->next;
s->next = C->next;
C->next = s;
}
else
{
s = q;
q = q->next;
s->next = C->next;
C->next = s;
}
}
/**
下边这两个循环是和求递增归并序列不同的地方,必须将剩余元素逐个插入C的头部才能得到最终的递减序列
*/
while (p != NULL)
{
s = p;
p = p->next;
s->next = C->next;
C->next = s;
}
while (q != NULL)
{
s = q;
q = q->next;
s->next = C->next;
C->next = s;
}
}
//查找链表C(带头结点)中是否存在一个值为x的结点,若存在,则删除该结点并返回1,否则返回0
int findAndDelete(LNode *C, int x)
{
LNode *p, *q;
p = C;
//查找部分开始
while (p->next != NULL)
{
if (p->next->data == x)
{
break;
}
p = p->next;
}
//查找部分结束
if (p->next == NULL)
{
return 0;
}
else
{
//删除部分开始
q = p->next;
p->next = p->next->next;
free(q);
//删除部分结束
return 1;
}
}
6、双链表的操作
//尾插法建立双链表
void createDlistR(DLNode *&L, int a[], int n)
{
DLNode *s, *r;
int i;
L = (DLNode*)malloc(sizeof(DLNode));
L->prior = NULL;
L->next = NULL;
r = L; //r始终指向终端结点,开始头结点也是尾结点
for (i=0; i<n; ++i)
{
s = (DLNode*)malloc(sizeof(DLNode));
s->data = a[i];
/**
下边3句将s插入到L的尾部,并且r指向s,s->prior = r;这一句是和建立单链表不同的地方
*/
r->next = s;
s->prior = r;
r = s;
}
r->next = NULL;
}
//在双链表中查找元素x,并返回结点指针
DLNode* findNode(DLNode *C, int x)
{
DLNode *p = C->next;
while (p != NULL)
{
if (p->data == x)
{
break;
}
p = p->next;
}
return p; //如果找到,则p中内容是结点地址(循环因break结束);如果没找到,则p中内容是NULL(循环因p等于NULL而结束)。
}
//插入结点的算法
//假设在双链表中p所指的结点之后插入一个结点s,其操作语句如下:
s->next = p->next;
s->next = p;
p->next = s;
s->next->prior = s; //假如p指向最后一个结点,则本行可去掉
//删除结点的算法
//设要删除双链表中p结点的后继结点,其操作语句如下:
q = p->next;
p->next = q->next;
q->next->prior = p;
free(q);