有序表就是表中的数据元素按照非递减或者非递增的规律进行排列。有序表的基本操作和线性表大致相同,并且有序表也可以有顺序表和链表两种存储表示方法。
在顺序有序表中进行插入操作 插入后仍保持其有序性
首先定义变量 i,令其值等于表尾数据的下标,使其从表尾开始遍历。进入循环,while (i >= 0 && x < L.elem[i]);在循环之中将数据右移一位同时 i--; 跳出循环的两种情况: 一:i=-1,此时直接将x插入到表头,二:x>=L.elem[i], 此时将x赋值给 L.elelm[i] 的后一个元素,最后表长加一。
本题建立的是有序的顺序表
#include<stdio.h>
const int LIST_INIT_SIZE = 100;
const int LISTINCREMENT = 10;
typedef struct {
int *elem;
int length;
int listsize;
int listincrementsize;
}SqList;
void InitList_Sq(SqList &L,int maxsize=LIST_INIT_SIZE,int incresize=LISTINCREMENT)
{
L.elem = new int[maxsize];
L.length = 0;
L.listsize = maxsize;
L.listincrementsize = incresize;
}
void OrdInsert_Sq(SqList &L, int x)
{
//在顺序表L中插入元素x,并且插入后仍满足有序 该有序表的数据时非递减的
int i = L.length - 1; //遍历有序顺序表时 从表为开始遍历
while (i >= 0 && x < L.elem[i])
{
L.elem[i + 1] = L.elem[i]; //值大于x的元素后移
i--;
}
L.elem[i + 1] = x; //跳出循环的两种情况 一:i=-1,此时直接将x插入到表头,二:x>=L.elem[i],此时将x赋值给L.elelm[i]的后一个元素
L.length++;
}
void main()
{
int i;
int x = 6;
SqList L;
InitList_Sq(L);
for (i = 0; i < 10; i++)
{
L.elem[i] = i + 1;
L.length++;
}
printf("表内容为:");
for (i = 0; i < 10; i++)
printf("%d ", L.elem[i]);
printf("\n");
OrdInsert_Sq(L, x);
printf("在有序表中插入数据6后表为:");
for (i = 0; i < 11; i++)
printf("%d ", L.elem[i]);
}
以有序表为集合 将B表中不同的元素提取到A表之中
本题思路是定义两个循环变量,从表L的前两个数据元素开始进行向后遍历。对于i,j,最初如果a[i]=a[j], 则j++,i 的值不变。然后再比较a[i] 和 a[j] 的值。如果不相等,就把 a[j] 插入到 a[i] 的后面。如果相等,则继续保持 i 的值不变,j 的值加一。一直循环就会通过覆盖,将L表中不同的元素都赶到表的前面部分。此时j=L.length 遍历到表的末尾,则跳出循环,结束。
本算法本质是将不同的元素通过向前一位覆盖的办法,完成不同元素与相同元素的分开,然后通过表长控制,去除掉多余的数据元素。即原地算法,也叫inplace算法。
本题建立的是有序的顺序表
#include<stdio.h>
const int LIST_INIT_SIZE = 100;
const int LISTINCREMENT = 10;
typedef struct {
int *elem;
int length;
int listsize;
int listincrementsize;
}SqList;
void InitList_Sq(SqList &L,int maxsize=LIST_INIT_SIZE,int incresize=LISTINCREMENT)
{
L.elem = new int[maxsize];
L.length = 0;
L.listsize = maxsize;
L.listincrementsize = incresize;
}
void purge_Osq(SqList &L)
{
//已知L是一个含有相同元素的顺序有序表,删除其中的相同元素
int i = 0, j = 1; //定义两个循环变量i,j,令它们最开始指向表L的前两个数据
while (j < L.length)
{
if (L.elem[i]!= L.elem[j])
{
L.elem[i + 1] = L.elem[j]; //将L.elem[j]插入到L.elem[i]之后
i++; //继续查找下一个元素
}
j++;
}
L.length = i + 1;
}
void main()
{
int i;
int x = 6;
SqList L;
int a[12] = { 1,2,3,4,4,5,6,7,7,8,9,10 };
InitList_Sq(L);
for (i = 0; i < 12; i++)
{
L.elem[i] = a[i];
L.length++;
}
printf("表内容为:");
for (i = 0; i < 12; i++)
printf("%d ", L.elem[i]);
printf("\n");
purge_Osq(L);
printf("经过删除表L内容为:");
for (i = 0; i < L.length; i++)
printf("%d ", L.elem[i]);
}
对于两个带头结点的单向循环有序链表A和B 求其对应集合的并集
定义三个指针pa,pb,rc。pa,pb指向集合A和B对应链表的结点,rc指向C当前的表尾结点。然后比较pa和pb所指结点的元素,如果pa->data < pb->data,说明pa所指的数据在B表中不可能出现,则应该将pa结点链接到C链表中(rc->next=pa),如果pa->data > pb->data,说明pb所指的数据在A表中不可能存在,所以将pb结点链接到C链表中 (rc->next=pb),如果pa->data = pb->data,则其中任意结点链接到C表中,并释放另一结点空间,直到有一个表被接入完,再接入另一个表的剩余段。
本题建立的是带有头结点的单向循环有序链表
#include<stdio.h>
typedef struct LNode {
int data;
struct LNode *next;
}LNode, *LinkList;
LinkList pHeada;
LinkList pHeadb;
void CreateList_L(LinkList &pHead, int a[], int n)
{
int i;
LinkList L;
pHead = new LNode;
pHead->data = NULL; //建立头结点
pHead->next = pHead; //头结点的后继指针指向自己
L = pHead; //全局变量L,用于标记链表开头
LNode *s; //用于新建结点
for (i = 0; i < n; i++)
{
s = new LNode;
s->data = a[i];
pHead->next = s; //将新建的结点接在头结点的后面
s->next = L; //将新建结点的后继指针指向表头 形成循环
pHead = s; //移动头指针pHead 将其指向新建结点
}
}
void union_OL(LinkList &La, LinkList &Lb)
{
//La和Lb分别为表示集合A和集合B的循环链表的头指针,求这两个集合的并集
//操作完成后La表示集合C的循环链表的头指针,集合A,B的循环链表都不复存在
LNode *pa, *pb, *rc, *qb,*pp; //pa和pb用于指向A表和B表,rc指向新表的当前结点,qb用于删除多余的结点,pp用于找到最初A表头结点的位置,便于后面形成循环
pa = La->next->next; //La指向的是表中的最后一个元素,所以pa指向的是第一个元素
pb = Lb->next->next;
rc = La->next; //rc指向的是表中的头结点
pp = rc;
while (pa != La->next&&pb != Lb->next)
{
if (pa->data < pb->data) //pa所指的数据更小,所以把pa接在rc的后面
{
rc->next = pa;
rc = pa;
pa = pa->next;
}
else if (pa->data > pb->data) //pb所指的数据更小,所以把pb接在rc的后面
{
rc->next = pb;
rc = pb;
pb = pb->next;
}
else //pa和pb所指的元素大小相等,将pa指向rc的后面,删除bp所指的结点
{
rc->next = pa;
rc = pa;
pa = pa->next;
qb = pb; //引入qb指向待删除的结点,防止pb后移后结点丢失
pb = pb->next;
delete qb;
}
}
if (pb == Lb->next) //链接A的剩余段
{
rc->next = pa;
}
else //链接B的剩余段
{
rc->next = pb;
pb = Lb->next; //pb指向B的头结点
Lb->next = pp;
La = Lb; //构成C的循环链
}
delete pb; //释放B表的表头
}
void main()
{
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
int b[10] = { 4,5,6,7,8,9,11,22,33,44 };
LNode *p;
CreateList_L(pHeada, a, 10);
printf("输出A集合:");
for (p = pHeada->next->next; p != pHeada->next; p = p->next)
printf("%d ", p->data);
printf("\n");
CreateList_L(pHeadb, b, 10);
printf("输出B集合:");
for (p = pHeadb->next->next; p != pHeadb->next; p = p->next)
printf("%d ", p->data);
printf("\n");
union_OL(pHeada, pHeadb);
printf("输出A和B的并集为:");
for (p = pHeada->next->next; p != pHeada->next; p = p->next)
printf("%d ", p->data);
}
判断两个用有序表表示的集合是否相等
本题算法较为简单,首先要定义两个指针,分别指向各自链表的第一个结点(头结点之后的那个结点),然后控制其相等的条件,再此条件下使其遍历结束,并且输出TRUE,否则不符合条件的输出FALSE.
本题建立的链表为带有头结点的单向有序链表
#include<stdio.h>
#define TRUE 1
#define FALSE 0
typedef struct LNode {
int data;
struct LNode *next;
}LNode, *LinkList;
void CreateList_L(LinkList &L, int a[], int n)
{
int i;
LNode *s;
L = NULL; //创建链表时一定不要忘记此处要让L=NULL!!!
for (i = n - 1; i >= 0; i--)
{
s = new LNode;
s->data = a[i];
s->next = L; //逆序创建时第一个结点是整个链表的尾节点,此时第一次是给其next指针赋值为NULL,第二次开始才是指向L所指结点
L = s;
}
s = new LNode;
s->data = NULL;
s->next = L;
L = s;
}
bool isequal_OL(LinkList A, LinkList B)
{
//指针A和B分别指向两个带头结点的单链表
//若两集合相同,则返回TRUE,否则返回FALSE
LNode *pa, *pb;
pa = A->next; //A和B都是指向头结点
pb = B->next;
while (pa&&pb&&pa->data == pb->data)
{
pa = pa->next;
pb = pb->next;
}
if (pa == NULL && pb == NULL)
return TRUE;
else
return FALSE;
}
void main()
{
LinkList La, Lb;
LNode *p;
int a[5] = { 1,2,3,4,5 };
int b[5] = { 1,2,3,4,5 };
CreateList_L(La, a, 5);
CreateList_L(Lb, b, 5);
if (isequal_OL(La, Lb) == 1)
printf("这两个集合相等。");
else
printf("这两个集合不相等。");
}
本笔记所依据的教材为严薇敏版的《数据结构及应用算法教程》
所有代码在Visual Studio 2017上均可正常运行
如有错误欢迎指出