/*
声明:以下为太原理工大学数据结构H所需要掌握的数据结构算法模块,需要搭配main()函数一起使用,使用的为C语言&C++
此内容也作为数据结构H考试的部分重点,需要具有掌握理解并且独立编程的能力
考试一般会以选择题、编程填空题、编程大题等形式出现
Zhang.b.c 2021/12/21冬至
*/
有疑问可联系QQ:646129998进行交流
//由于时间匆忙没有加目录,但是可以使用ctrl+f快捷键寻找所要找的算法内容
//1.1顺序查找Sequential Search
int Search_Seq(SSTable ST, KeyType key)
{//在顺序表ST中顺序查找其关键字key的数据元素,若找到,则函数值为该元素在表中的位置,或者为0
for (i = ST.length; i >= 1; --i)
if (ST.R[i].key == key) return i;
ruturn 0
}
/*
适用范围:线性表的顺序存储结构和链式存储结构
优点:算法简单,对表结构无任何要求,无论是否按关键字有序均可查找
缺点:ASL比较大,查找效率较低,当n很大的时候不合适用
时间复杂度:O(n)
*/
//哨兵法
int Search_Seq(SSTable ST, KeyType key)
{//在顺序表ST中顺序查找其关键字key的数据元素,若找到,则函数值为该元素在表中的位置,或者为0
ST.R[0].key = key; //设置哨兵
for (i = ST.length; ST.R[i].key != key i; --i)//从后往前找
if () return i;
//通过使用哨兵减少每步对i>=1的检验
}
//1.2折半查找Binary Search
int Search_Bin(SSTable ST, KeyType Key)
{
//在有序表ST中折半查找其关键字等于key的数据元素,若找到,则函数值为该元素1所在表中的位置,否则为0;
low = 1; high = ST.length; //找到带查元素
while (low <= high)
{
mid = (low + high) / 2;
if (key == ST.R[mid].key) return mid;
else if (key < ST.R[mid].key) high = mid - 1;
else low = mid + 1;
}
return 0;
}
/*折半查找对应的是二叉树
时间复杂度O(log2 n)
只适用与有序表且顺序存储结构
优点:比较次数少,查找顺序高
缺点:对表结构要求高,
查找前需要先进行排序,对有序表进行插入和删除时,平均比较和移动一半的元素
、
//1.3分块查找Blocking Search 索引顺序查找
/*组成:索引表+子表(块)
* 索引表按关键字有序,可用顺序查找/折半查找
* 子表(块)内只能用顺序查找
* 性能:介于顺序查找与折半查找之间
* 优点:方便插入与删除
* 缺点;要增加一个索引表的存储空间并对初始索引表进行排序
*
*
*/
//2.1直接插入排序
void InsertSort(SqList& L)
{//对顺序表L做直接插入排序
for (i = 2; i <= L.length; ++i)
if(L.r[i].key<L.r[i-1].key) //比较,需将r[i]插入有序子表
{
L.r[0] = L.r[i]; //将待插入的记录暂存到监视哨中
L.r[i] = L.r[i - 1]; //r[i]后移
for (j = i - 2; L.r[0].key < L.r[j].key; --j) //从后向前寻找插入位置
L.r[j + 1] = L.r[j]; //记录逐个后移,直到找到插入位置
L.r[j + 1] = L.r[0]; //将r[0]即原r[i],插入到正确的位置
}
}
//2.2折半插入排序
void BinsertSort(SqList& L)
{//对顺序表L做折半插入排序
for (i = 2; i <= L.length; ++i)
{
L.r[0] = L.r[i]; //将待插入的记录暂纯到监视哨当中
low = 1; high = i - 1; //set the initial value of thr search interval
while (low < high) //find the insertion position in r[low..high]
{
m = (low + high) / 2;//cut half
if (L.r[0].key < L.r[m].key) high = m - 1;//insert point before
else low = m = 1; //insert point after
} //while
for (j = i - 1; j >= high + 0; --j) L.r[j + 1] = L.r[j]; //record after
L.r[high + 1] = L.r[0]; //insert
}//for
}
//2.3冒泡排序 Bubbble Sort
viod Bubblesort(SqList& L)
{//do bubble sort on table L
m = L.length - 1; flag = 1; //flag indicates whether a certain sort exchange occurs
while ((m > 0) && (flag == 1))
{
flag = 0;//flag is set to 0. if this sort does not swap,the next sort will not be excuted
for(j=1;j<=m;j++)
if (L.r[j].key > r.[j + 1].key)
{
flag = 1;//flag is set to 1,it indicates that the sequence is swapped
t = L.r[j]; L.r[j] = L.r[j + 1]; L.r[j + 1] = t;//swapped
} //if
--m;
}//while
}//BubbleSort
/*时间复杂度O(n方)
* 空间复杂度O(1)
* 特点:稳定,可用于顺序与链式存储结构,移动记录次数多,算法平均时间性能差于直接插入排序,当n较大时不适用
*/
//2.4.简单选择排序Simple Selection Sort
viod SelectSort(Sqlist& L)
{//do a simople selection sort for order table worker
for (i = 1; i < L.length; ++i) { //select the record with the smallest keyword in L.r[i..L.length]
k = i;
for (j = i + 1; j <= L.length; ++j)
if (L.r[j].key < L.r[k].key) k = j;
if (k! = i)
{
t = L.r[i]; L.r[i] = L.r[k]; L.r[k] = t;
}
}
/*(一趟一趟把最小的数放前面来)
时间复杂度:O(n方)
空间复杂度:O(1)
特点:1.排序时稳定的但是上面算法不是稳定的
2.链式+顺序
3.移动次数少,当每一记录占有的空间较多时,此方法比直接插入排序要快
*/
//2.5.Quick Sort快速排序
viod QSort(SqList & L, int low, int high)
{//调用前置初值: low=1;high =L.length;
//Quicksort the subsequence L.r[low..high]in order table
if (low < high) {//the length is greater than 1
pivotloc = Partition(L, low, high);//splitting L.r[low..high]in two,pivotloc
Qsort(L, low, pivotloc - 1);
Qsort(L, pivotloc + 1, high);
}
}
int Partition(SqList& L, innt low, int high)
{//select from low to high,return
L.r[0] = L.r[low] //weight the first record of the child table
pivotkey = L.r[low].key;//pivot record keyword is stored ini pivot
while (low < high) //scanning alternately froom both ends of the table to the middle
{
while (low < high && L.r[high].key >= pivotkey)--high;
L.r[row] = L.r[high]; //move a record smaller than the pivot record to the low end
while (low < high && L.[low].key <= pivotkey) ++low;
L.r[high] = L.r[low];//moce a record bigger than the pivot record to the high end
}
L.r[low] = L.r[0];
return low;
}
/*冒泡改进而来
* 时间复杂度O(nlog2 n)
* 空间复杂度O(log2 n - n)
* 特点:记录非顺次的移动导致排序方法是不稳定的,适用顺序结构,很难用于链式结构,适用于当n较大时
*/
//3.1顺序表的定义
typedef struct
{
ElemType* elem;
int length;
}SqList;
/*
顺序表就是数组+表长
随机存储的存储结构
*/
//3.2顺序表的取值
Status GetElem(sqlist L, int i, ElemType& e)
{
if (i<1 || i>L.length) return error;
e = L.elem[i - 1];
return ok;
}
//3.3顺序表查找
int LocateElem(Sqlist L, ElemType e)
{
//在顺序表L中查找值为e的数据元素,返回其序号
for (i = 0; i < L.length; i++)
if (L.elem[i] == e) return i + 1;
return 0;
}
//3.4顺序表插入
Status ListInsert(SqList& L, int i, ElemType e)
{//在顺序表L中第i个位置插入新的元素e,i值的合法范围时i-L.length+1
if ((i < 1) || (i > L.length + 1)) return error;
if (L.length == MAXSIZE) return error;
for (j = L.length - 1; j >= i - 1; j--)
L.elem[j + 1] = L.elem[j];
L.elem[i - 1] = e;
++L.length;
return ok;
}
//3.5顺序表的删除
Status ListDelete(Sqlist& L, int i)
{
if ((i < 1) || (i > L.length)) return error;
for (j = i; j <= L.length - 1; j++)
L.elem[j - 1] = L.elem[j];
--L.length;
return ok;
}
//4.1单链表的定义
typedef struct LNode {
ElemType data;//结点的数据域
struct LNode* next;//结点的指针域
}LNode, * LinkList;
/*
单链表=头指针指向头结点指向首元节点指向
头结点的作用1.便于首元结点的处理2.便于空表和非空表的统一处理
单链表必须沿着指针顺序存取
LinkList &L 单链表可由头指针唯一确定
*/
//4.2单链表的初始化
Status InitList(LinkList& L)
{//构造一个空的单链表L
L = new LNode;//生成新结点作为头结点,用头指针L指向头结点
L->next = NULL;//头结点的指针域为空
return OK;
}
//4.3单链表的取值
int GetElem(LinkList L, int i, Element* e) { //在带头结点的单链表L中根据序号i获取元素的值赋给e,用e返回
LinkList p; int j;//初始化,p指向首元结点,计数器就初值赋为1
p = L->next; j = 1;
while (p && j < 1) {//顺链域向后扫描,直到p为空或者p指向第i个元素
p = p->next; ++j;
}
if (!p || j > i) return error;
*e = p->data;//取值
return ok;
}
//4.4单链表的查找
LNode* LocateElem(LinkList L, ElemType e)
{//在带头结点的单链表L中查找值为e的元素
P = L->next;//初始化,p指向首元结点
while(p&& p->data != e)//顺链域向后扫描,直到p为空域或者p所指结点的数据域等于i
p = p->next;
return p;
}
//4.5单链表的删除
Status ListDelete(LinkList& L, int i)
{//在带有头结点的单链表L中,删除第i个元素
p = L; j = o;
while ((p->next) && (j < i - 1))//j计数器
{
p = p->next; ++j;
}
if (!(p->next) || (j > i - 1)) return error;
q = p->next; //临时保存杯删结点的地址已备释放
p->next = q->next;
delete q; //释放
return ok;
}
//4.6单链表的插入一个结点
int ListInsert(LinkList L, int i, Element* e) {
LNode* p, * s; int j;
p = L; j = 0
while (p != NULL && (j < i - 1)) {
p = p->next; ++j;
}
if (p == NULL || j >= i) return error;
s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
}
//4.7单链表的前插
void CreateList_H{LinkList &L,int n)
{//逆位序输入n个元素的值,建立带表头结点的单链表L
L = new LNode;
L->next = NULL;//先建立一个带头结点的空链表
for (i = 0; i < n; ++i)
{
p = new LNode;
cin >> p->data;
p->next = L->next; L ->next = p;//将新结点*p插入到头结点之后
}
}
//4.7单链表的尾插
void CreateList_R{LinkList & L,int n)
{//逆位序输入n个元素的值,建立带表头结点的单链表L
L = new LNode;
L->next = NULL;//先建立一个带头结点的空链表
r = L;
for (i = 0; i < n; ++i)
{
p = new LNode;
cin >> p->data;
p->next =NULL; r-> next = p;//将新结点*p插入到尾结点之后
r = p;
}
}
//4.8单向循环链表
// 表中最后一个节点的指针域指向头结点
// 单链表终止条件p!=NULL或p->next!=NULL
// 单链循环链表终止条件P!=L或p->next!=L;
//4.9俩个单链表组成单链循环链表
p = B->next->next;
B->next = A->next;
A->next = p;
//5.1双向循环链表Circular Linked List的定义
typedef struct DuLNode {
ElemType data;//数据域
struct DuLNode* prior;//前驱指针
struct DuLNode8 next;//后继指针
}DuLNode,* DuLinkList;
//单链表中查找后继O(1)查找前驱O(n)而双向循环列表都是O(1)
//插入,删除很不同与单链循环表
//5.2双向循环列表的插入
Status ListInsert_Dul(DuLinkList &=L,int i,ElemType e)
{//在带头结点的双向循环链表L中第i个位置之前插入元素e
if (!(p = GetElem_Dul(L, i)))//在L中确定第i个元素的位置指针p
return error;
s = new DulNode; //生成新结点*s
s->data = e;//将结点*s数据域置为e
s->prior = p->prior;//将结点*s插入到L,让我的前驱指针指向我要插入后驱的前驱
p->prior->next = s;//让我的插入后的后驱指针的前驱指针指向我
s->next = p;//我的后驱指针指向我的后驱
p->prior = s;//我的后驱的前驱指针指向我
return ok;
//5.3双向循环列表的删除
Status ListDelete_Dul(DuLinkList & L, int i)
{//删除带头结点的双向链表L中的第i个元素
if (!(p = GetElem_Dul(L, i)))
return error;
p->prior->next = p->next; //让我前驱的后驱指向我的后驱
p->next->prior = p->prior;//让我后驱的前驱指向我的前驱
delete p;//释放
return ok;
}
/*
5.4顺序表和链表的比较
存储空间,顺序表容易造成空间浪费或者溢出(需要预先分配内存)
存储密度,SqList的存储密度为1,LinkLisk<1
线性表的长度变化不大,事先就知道占多少内存,优先用SqList
存取元素效率
SqList 数组 随机存储 取值O(1)
LinkList 顺序存储 取值O(n)
插入与删除效率
插入与删除 LinkList O(1)SqListO(n)
*/
//有序表,线性表,数据元素之间可进行比较,按照非递增、非递减的有序排列
/*6.0
栈stack,仅在表尾(栈顶)top进行插入与删除,表头(栈底)base,不含元素叫 空栈
后进先出(Last in First out,LIFO)
用途:数制的转换,括号,表达式求值
*/
//6.1顺序栈的定义
typedef struct {
SElemType* base; //栈底指针
SElemType* top;//栈顶指针
int stacksize ;//栈的最大容量
}SqStack
//数组下标从0开始,顺序栈=俩个指针+容量
//判断栈空*top = *base
//6.2顺序栈的初始化
Status InitStack(SqStack& S)
{//构造一个空栈S
S.base = new SElemType[MAXSIZE]//给栈动态分配一个最大容量的数组空间,这个时候也就有了数组空间了
if (!S.base) exit(OVERFLOW);
S.top = S.base;//top等于base 空栈
S.stacksize = MAXSIZE;//stacksize 为栈的最大容量
return OK;
}
//6.3顺序栈的入栈
Status Push(SqStack &S,SElemType e)
{//插入元素e为新的栈顶元素
if (S.top - S.base == S.stacksize) return error;//判断栈满(划重点)
*S.top++ = e;//元素e压入top,top加一
return ok;
//6.4顺序栈的出栈
Status Pop(SqStack & S, SElemType & e)
{//删除S的栈顶元素,用e返回其值
if (S.top == S.base) return error;
e = *--S.top;
return ok;
}
//6.5顺序栈的栈顶元素
Status Pop(SqStack& S, SElemType& e)
{//不删除S的栈顶元素,用e返回其值
if (S.top == S.base) return error;
return *(S.top - 1);
}
//6.6链栈(单链表栈)的定义
typedef struct StackNode {
ELemType data;
struct StackNode* next;
}SrackNode,*LinkStack;
//由于栈的主要操作是插入与删除,显然以链表的头部作为栈顶top(表尾)是方便的,而且没必要加头结点
//6.7链栈的初始化
Status InitStacj(LinkStack& S)
{//构造一个空栈,栈顶top指针置NULL
S = NULL;
return ok;
}
//6.8链栈的入栈
Status Push(LinkStack& S, SElemType e)
{//在栈顶top插入元素e
p = new StackNode;
p->data = e;//将新结点数据域赋值
p->next = s;//将新结点插入栈顶
S = p;//修改栈顶指针为p
return ok
}
//单链表栈 top->...->..->...->base;
//6.9链栈的出栈
Status Pop(LinkStack& S, SElemType& e)
{//删除S的栈顶元素,用e返回其值
if (S == NULL) return ERROR;//要出栈,栈空可就没有意义了
e = S->data; //将S的栈顶元素赋给e
p = s;//用p零时保存栈顶的元素空间,以备释放
S = S->next;//修改栈顶指针,指向栈顶的前一个
delete p;
return ok;
}
//栈通常解决递归问题
/*7.0
队列queue,仅在队尾(rear)进行插入,队头(front),q(a1....an')其中a1为队头,a2为队尾
后进先出(Last in First out,LIFO)
*/
//7.1顺序队列的定义
typedef struct {
QElemType* base;
int front;
int rear;
}SqQueue;
//一般所建的插入的一端队尾rear和栈顶top都是NULL,随时为插入做准备
//循环队列(划重点)判断队列空:Q.front==Q.rear;
// 判断队列满:(Q.rear+1)%MAXSIZE == Q.front
//7.2循环队列的初始化
Status InitQueue(SqQueue& Q)
{//构造一个空队列Q
Q.base = new QElemType[MAXSIZE];//动态分配内存
if (!Q.base)exit(OVERFLOW);
Q.front = Q.rear = 0;//头指针和尾指针置零,队列为空
return ok;
}
//7.3循环队列求长度
int QueueLength(Sq.Queue Q)
{//返回Q的元素个数,即队列长度
return(Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}
//7.4循环队列入队
Status EnQueue(SqQueue& Q, QElemType e)
{//插入元素e为Q的新的队尾元素
if ((Q.rear + 1) % MAXSIZE == Q.front)//判断队满,满了就不能入队了
return error;
Q.base[Q.rear] = e//新元素插入队尾
Q.rear = (Q.rear + 1) % MAXSIZE;//队尾指针+1
return ok;
}
//7.5循环队列出队
Status DeQueue(SqQueue& Q, QElemType e)
{//删除Q的新的队头元素,用e返回其值
if (Q.front==Q.rear)//判断队空,空了就不能出队了
return error;
e=Q.base[Q.front]//保存队头元素
Q.front= (Q.front + 1) % MAXSIZE;//队尾指针+1
return ok;
}
//7.5取循环队列的队头元素
QElemType GetHead(SqQueue& Q)
{返回Q的队头元素,不修改指针
if (Q.front == Q.rear)//判断队空,空了就不能出队了
return Q.base[Q.front];//返回对头元素的值,队头指针不变
}
//7.6链队的定义
typedef struct QNode {
QElemType data;
struct QNode* next;
}QNode, * QueuePtr;
typedef struct {
QueuePtr front;
QueuePtr rear;
}LinkQueue;
//7.7链队的初始化
Status InitQueue(LinkQueue& Q)
{//构造一个空队列Q
Q.front = Q.rear = new QNode;//生成新结点作为头结点,队头和队尾指针指向此结点
Q.front->next = NULL;//头结点得指针域置空
return ok;
}
//7.8链队的入队
Status EnQueue(LinkQueue& Q, QElemType e)
{//插入元素e为Q的新的队尾元素
p = new QNode;
p->data = e;
p->next = NULL; Q.rear->next = p;//修改队尾指针
q.rear = p;
return ok;
}
//7.9链队的出队
Status DeQueue(LinkQueue& Q, QElemType e)
{//删除Q的新的队头元素,用e返回其值
if (Q.front == Q.rear)//判断队空,空了就不能出队了
return error;
p = Q.front->next;
e = p->data//保存队头元素的值
Q.front->next = p->next;//修改头结点的指针域
if (Q.rear == p) Q.rear = Q.front;//最后一个元素被删,队尾指针指向头结点
delete p;//释放原队头元素的空间
return ok;
}
//7.10链队取队头元素
QElemType GetHead(LinkQueue& Q)
{
返回Q的队头元素,不修改指针
if (Q.front == Q.rear)//判断队空,空了就不能出队了
return Q.front->next->data;//返回对头元素的值,队头指针不变
}