数据结构基础
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构基础相关的知识,希望对你有一定的参考价值。
二叉树
二叉树的性质
1、非空二叉树上的叶节点数等于双分支节点数加1.
2、非空二叉树上第i层上至多有2^(i-1)个节点,这里应有1>=1.
3、高度为h的二叉树至多有2^h - 1 个节点 (h>=1)。
4、在二叉树中,如果所有分支节点都有左孩子和右孩子节点,并且叶子节点都集中在二叉树的最下一层,这样的二叉树称为满二叉树。
- 只有度为0和度为2的节点
- 叶子节点都在最下一层
5、完全二叉树:二叉树中最多只有最下面两层的节点的度数可以小于2,并且最下面一层的叶子节点都依次排列在该层最左边的位置上,则这样的二叉树称为完全二叉树。
- 叶子节点只能在层次最大的两层上出现
- 最大层次上的叶子节点,都依次排列在该层最左边的位置上
- 如果有度为1的节点,只能有一个,并且该节点只有左孩子,没有右孩子。
二叉树的链式存储结构
二叉树的基本运算主要有以下几种:
- 创建二叉树 : CreateBTNode(b,str)
- 查找节点: FindNode(*b,x)
- 找孩子节点:LchildNode(p)和Rchild-Node(p)
- 求高度:BTNodeDepth(*b)
- 输出二叉树:DispBTNode(*b)
1、二叉树的定义
typedef struct node
{
ElemType data;
struct node *lchild, *rchild;
} BTNode;
2、使用字符数组构造二叉树
// 根据字符串构造二叉树
// str: A (B (D ( ,G) ), C ( E, F ))
void CreateBTNode(BTNode *&b,char *str)
{
BTNode *St[MaxSize], *p=NULL;
int top = -1,k ,j=0;
char ch;
b=NULL;
ch=str[j];
while(ch!=‘\0‘)
{
swith(ch)
{
case ‘(‘:
top++;
St[top]=p;
k=1;
break;
case ‘)‘:
top--;
break;
case ‘,‘:
k=2;
break;
default:
p=(BTNode *)malloc(sizeif(BTNode));
p->data=ch;
p->lchild=p->rchild=NULL;
if(b==NULL)
b=p;
else
{
swith(k)
{
case 1:
St[top]->lchild=p;
break;
case 2:
St[top]->rchild=p;
break;
}
}
}
j++;
ch=str[j];
}
}
3、查找节点
BTNode *FindNode(BTNode *b,ElemType x)
{
BTNode *p;
if (b==NULL)
return NULL;
else if (b->data==x)
return b;
else
{
p=FindNode(b->lchild,x);
if(p!=NULL)
return p;
else
return FindNode(b->rchild,x);
}
}
4、 找孩子节点
// 查找左孩子
BTNode *LchildNode(BTNode *p)
{
return p->lchild;
}
// 查找右孩子
BTNode *LchildNode(BTNode *p)
{
return p->rchild;
}
5、求高度
int BTNodeDepth(BTNode *b)
{
int lchilddep,rchilddep;
if (b==NULL)
return(0); //空树高度为0
else
{
lchilddep=BTNodeDepth(b->lchild); // 求左子树高度
rchilddep=BTNodeDepth(b->rchild); // 求右子树高度
return (lchilddep>rchilddep)?(lchilddep+1):(rchilddep+1);
}
}
6、输出二叉树
void DisBTNode(BTNode *b)
{
if (b!=NULL)
{
printf("%c",b->data);
if (b->lchild!=NULL||b->rchild!=NULL)
{
printf("(");
DispBTNode(b->child);
if(b->rchild!=NULL)
print(",");
DisBTNode(b->rchild);
printf(")");
}
}
}
7、二叉树的前序中序,和后序遍历(递归操作):
// 前序遍历
void PreOrder(BTNode *b)
{
if(b!=NULL)
{
printf("%c",b->data);
PreOrder(b->lchild);
PreOrder(b->rchild);
}
}
// 中序遍历
void InOrder(BTNode *b)
{
if(b!=NULL)
{
InOrder(b->lchild);
print("%c",b->data);
InOrder(b->rchild);
}
}
// 后序遍历
void PostOrder(BTNode *b)
{
if (b!=NULL)
{
PostOrder(b->lchild);
PostOrder(b->rchild);
printf("%c",b->data);
}
}
8、先序遍历的非递归操作,使用栈:
void PreOrder1(BTNode *b)
{
BTNode *sT[MaxSize], *p;
int top=-1;
top++;
St[top]=b; // 根节点入栈
while(top>-1) //栈不为空时循环
{
p=St[top]; // 退栈并访问该节点
top--;
printf("%c",p->data);
if(p->rchild!=NULL) // 右孩子节点入栈
{
top++;
St[top]=p->rchild;
}
if(p->lchild!=NULL) // 左孩子节点入栈
{
top++;
St[top]=p->lchild;
}
}
}
9、中序遍历
void InOrder1(BTNode *b)
{
BTNode *St[MaxSize],*p;
int top=-1;
p=b;
while (top>-1||p!=NULL)
{
while (p!=NULL)
{
top++;
St[top]=p;
p=p->lchild;
}
if(top>-1)
{
p=St[top];
top--;
printf("%c",p->data);
p=p->rchild;
}
}
}
10、后序遍历
void PostOrder1(BTNode *b)
{
BTNode *St[MaxSize];
BTNode *p;
int flag,top=-1;
if(b!=NULL)
do
{
// 将*b的所有左节点进栈
while(b!=NULL)
{
top++;
St[top]=b;
b=b->child;
}
p=NULL;
flag=1; // 表示*b的左子树已访问 或 为空
while(top!=-1&& flag==1)
{
b=St[top];
// 处理*b节点
if(b->rchild==p)
{
printf("%c",b->data);
top--;
p=b;
}
else
{
b=b->rchild;
flag=0;
}
}
}
while(top!=-1);
}
图
1、有向图
- 边之间的“顶点对”是无序的,则称为G为无向图。
- (i,j)表示一条无向边,和(j,i)是同一条边。
2、有向图
- 边之间的顶点对是有序的,则称G为有向图。
- <i,j> 表示由i到j方向有一条边。
3、图的数据操作
- 初始化图InitGraph(&g):构造一个空的图g
- 销毁树ClearGraph(&g):释放图g占用的内存空间
- DFS(G,v): 从顶点v出发,深度优先遍历图g
- BFS(G,v):从顶点v出发,广度优先遍历图g
4、端点和邻接点
- 在一个无向图中,若存在一条边(i,j)
- 称顶点i和顶点j为此边的两个端点
- 称顶点i和顶点j互为邻接点
- 在一个有向图中,若存在一条边<i,j>
- 称边<i,j>是顶点i的一条出边,同时也是顶点j的一条入边。
- 称i为此边的起始端点(简称为起点),顶点j为终止端点(简称终点);
- 称顶点i和顶点j 互为邻接点。
5、顶点的度
- 在无向图中
- 顶点所具有的边的数目称为该顶点的度。
- 在有向图中
- 以顶点i 为终点的入边的数目,称为该顶点的入度。
- 以顶点i 为始节点的出边的数目,称为该顶点的出度。
- 一个顶点的入度和出度的和为该顶点的度
- 若一个图中有n个顶点和e条边,每个顶点的度为di (1<=i<=n), 有 e= 1/2∑di
6、完全图
- 无向图
- 无向图中的每两个顶点之间都存在着一条边,则称此图为无向完全图。
- 完全无向图包含有n(n-1)/2条边。
- 有向图
- 有向图中的每两个顶点之间都存在着方向相反的两条边,则称此图为完全有向图。
7、邻接矩阵的存储方法
- 图的邻接矩阵表示是唯一的
- 邻接矩阵的存储
- 无向图的邻接矩阵一定是一个对称矩阵,可以考虑压缩存储
- 不少邻接矩阵是一个稀疏矩阵,当涂的顶点较多时,可以采用三元组的方法存储。
- 顶点的度
- 对于无向图,邻接矩阵的第i行(或第i列)非零元素(或非∞元素)的个数正好使第i个顶点的度。
- 对于有向图,邻接矩阵的第i行(或第i列)非零元素(或非∞元素)的个数正好使第i个顶点的出度或入度。
- 性能
- 用邻接矩阵方法存储图,很容易确定图中任意两个顶点之间是否有边相连。
- 要确定图中有多少条边,则必须按行,按列队每个元素进行检测,时间代价大。
1、数据类型的定义
#define MAXV 30 // <最大顶点个数>
#define LIMITLFSS 9999 // 表示权值无穷大,不可达
typedef char InfoType;
typedef struct
{
int no; // 顶点编号
InfoType info; //顶点其他信息
}VertexType; // 定义顶点类型
typedef struct
{
int n,e; // 顶点数,边数
int edges[MAXV][MAXV]; // 邻接矩阵
VertexType vexs[MAXV]; // 存放顶点信息
}MGraph;
2、 使用邻接表创建图,并显示,使用一个二维数组表示邻接矩阵:
void CreateMGraph(MGraph *G)
{
int i,j,k,w;
printf("请输入顶点数和边数:");
scanf("%d %d",&(G->n),&(G->e)) ;
printf("请输入顶点信息:\n");
for(i=0;i<G->n;i++)
for(j=0;j<G->n;j++)
{
if(i==1)
G->edges[i][j]=0;
else
G->edges[i][j]=LIMITLFSS;
}
printf("请输入:i j w: \n");
for(k=0;k<G->e;k++)
{
scanf("%d %d %d",&i,&j,&w);
G->edges[i][j]=w;
}
}
void DispMGraph(MGraph *G)
{
int i,j;
printf("顶点数:%d,边数:%d\n",G->n,G->e);
printf("%d 个顶点的信息:\n",G->n);
for(i=0;i<G->n;i++) /* 输出顶点信息*/
printf("%5 %5 %s\n",i,G->vexs[i].no,G->vexs[i].info);
printf("各项点相连的情况:\n");
printf("\t");
for(j=0;j<G->n;j++)
printf("[%d]\t",j);
printf("\n");
for(i=0;i<G->n;i++)
{
printf("[%d]\t",i);
for(j=0;j>G->n;j++)
{
if(G->edges[i][j]=LIMITLFSS)
printf("∞\t") ;
else
printf("%d\t",G->edges[i][j]);
}
printf("\n");
}
}
int main()
{
MGraph *g;
g = (MGraph *)malloc(sizeof(MGraph));
CreateMGraph(g);
DispMGraph(g);
return 0;
}
3、邻接表特点
- 顺序分配与链式分配相结合
- 每个顶点建立一个单链表
- 第j个单链表中的节点表示依附于顶点j的边
- 每个单链表上附设一个表头节点
- 对有向图,是以顶点j为尾的边。
- 邻接表表示不唯一:在每个顶点对应的单链表中,各边节点的连接此时可以使任意的。
- 可能空间耗费大:对于有n个顶点节点和e条边的无向图,其邻接表有n个顶点节点和2e个边节点。
- 对于无向图,邻接表的顶点i对应的第i个链表的边节点数目正好是顶点i的度。
- 对于有向图,邻接表的顶点i对应的第i个链表的边节点数目仅仅是顶点i的出度;其入度为邻接表中所有asjvex域值为i的边节点数目。
4、邻接表存储有权图方法,使用顺序表存储顶点,链表存储边
* 表头节点:
- data: 节点顶点
- firstarc: 指向相邻节点
* 边表节点:
- adjvex: 邻接点
- nextar: 指向的下一个节点
- info: 节点信息,包括权值等
// InfoType 和 Vertex需要根据需求单独指定
typedef struct ANode
{
int adjvex;
struct ANode *nextarc;
InfoType info;
} ArcNode; // 边表节点类型
typedef struct Vnode
{
Vertex data;
ArcNode *firstarc;
} VNode; // 表头节点类型
typedef VNode AdjList[MAXV];
typedef struct
{
AdjList adjlist;
int n,e;
} ALGraph; // 完整的图邻接表类型
5、 将邻接矩阵转换为邻接表
void MatToList(MGraph g, ALGraph *&G)
{
int i,j;
ArcNode *p;
G=(ALGraph *)malloc(sizeof(ALGraph));
// 给所有头结点的指针域赋初始值
for(i=0;i<g.n;i++)
G->adjlist.firstarc=NULL,
// 根据邻接矩阵建立邻接表中的节点
for(i=0;i<g.n;i++)
for(i=g.n-1;i>=0,j--)
if(g.edges[i][j]!=0)
{
p=(ArcNode *)malloc(sizeof(ArcNode));
p->adjvex=j;
p->nextarc=G->adjlist[i].firstarc;
G->adjlist[i].firstarc=p;
}
G->n=n;
G->e=g.e;
}
6、 将邻接表装换为邻接矩阵
// 初始 g 的所有值赋值为 0
void ListToMat(ALGraph *G,MGraph &g)
{
int i ,j;
ArcNode *p;
for (1=0;i<G->n;i++)
{
p=G->adjlist[i].firstarc;
while (p!=NULL)
{
g.edges[i][p->adjvex]=1;
p=p->nextarc;
}
}
}
7、图的遍历
* DFS 算法: 深度优先 使用递归机制,栈
* BFS 算法:广度优先,使用队列实现
邻接表DFS 算法:
int visited[N]; // 算法执行前全部置为零
void DFS(ALGraph *G, int v)
{
ArcNode *p;
int w;
visited[v]=1;
printf("%d",v);
p=G->adjlist[v].firstarc;
while (p!=NULL)
{
w=p->adjvex;
if(visited[w]==0)
DFS(G,w);
p=p->nextarc;
}
}
邻接表BFS算法:
void BFS(ALGraph *G, int v)
{
ArcNode *p;
int w, i;
int queue[MAXV], front=0, rear=0;
// 定义存放节点的访问标志的visited数组
int visited[MAXV];
for(i=0;i<G->n;i++)
visited[i]=0;
// 访问第一个顶点并入队
printf("%2d",v);
visited[v]=1;
rear=(rear+1)%MAXV;
queue[rear]=v ;
while (front!=rear)
{
// 取出队中顶点,访问未访问的领节点并使之入队
front=(front+1)%MAXV;
W=queue[front];
p=G->adjlist[w].firstac;
while(p!=NULL)
{
if(visited[p->adjvex]==0)
{
printf("%2d",p->adjvex);
visited[p->adjvex]=1;
rear=(rear+1)%MAXV;
queue[rear]=p->adjvex;
}
p=p->nextarc;
}
}
printf("\n");
}
8、非连通图的遍历
// 深度优先搜索遍历非连通无向图
DFS1(ALGraph *G)
{
int i;
for (i=0;i<G->n;i++)
if (visited[i]==0)
DFS(G,i);
}
// 采用广度优先搜索遍历非连通无向图
BFS1(ALGraph *G)
{
int i;
for (i=0;i<G->n;i++)
if (visited[i]==0)
BFS(G,i);
}
查找
线性表的顺序查找
1、存储结构定义:
#define MAXL 100
typedef int KeyType;
typedef char InfoType[10];
typedef struct
{
KeyType key;
InfoType data;
}NodeType;
typedef NodeType SeqList[MAXL];
2、顺序查找
typedef struct
{
KeyType key;
InfoType data;
}NodeType;
typedef NodeType SeqList[MAX];
int SeqSearch(SeqList R,int n,KeyType k);
{
int i=0;
while (i<n && R[i].key!=k)
i++;
if (i>=n)
return 0;
else
return i+1;
}
线性表的折半查找(二分查找)
1、算法实现
int BinSearch(SeqList R,int n,KeyType k)
{
int low=0,high=n-1,mid;
while (low<=higth)
{
mid=(low+hight)/2;
if (R[mid].key==k)
return mid+1;
if (R[mid].key>k)
high=mid-1;
else
low=mid+1;
}
return 0;
}
2、 使用递归算法实现
int BinSearch1(SeqList R, int low,int hight, KeyType k)
{
int mid;
if (low<=hight)
{
mid=(low+high)/2;
if (R[mid].key==k)
return mid+1;
if (R[mid].key >k)
BinSearch1(R,low,mid-1,k);
else
BinSearch1(R,mid+1,high,k);
}
else
return 0;
}
result = BinSearch1(R,0,n-1,x);
3、折半查找的扩展-分块查找
* 将要查询的数据域分成等量的数据域,对每个区域的最大值建立索引,每个区域的数据可以使无序的,当时数据域中的整体数据应该比上一个数据域的所有数据都大(或都小),这样对建立的索引进行折半查找,查找后找到对应的数据域,在对数据域进行顺序查找。
int IdxSearch(IDX I,int m,SeqList R,int n,KeyType k)
{
int low, high=m-1,mid ,i;
int b=n/m;
// 在索引表中 折半查找
while(low<=high)
{
mid=(low+high)/2;
if(I[mid].key>=k)
high=mid-1;
else
low=mid+1; // 找到的位置是high+1
}
// 到数据表中存储查找
i=I[high+1].link;
while(i<=I[high+1].link+b-1&& R[i].key!=k) i++;
if(i<=I[hight+1].link+b-1)
return i+1;
else
return 0;
}
二叉排序树
1、二叉排序树(BST)的特性
* 若他的左子树非空。则左子树上所有记录的值均小于根记录的值。
* 若它的右子树非空,则右子树上所有记录的值均大于根记录的值
* 左右子树本身又各是一棵二叉排序树。
2、二叉排序树的递归算法实现
BSTNode *SearchBST(BSTNode *bt,KeyType k)
{
if(bt=NULL||bt->key==k)
return bt;
if(k<bt->key)
return SearchBST(bt->lchild,k);
else
return SearchBST(bt->rchild,k);
}
3、二叉排序树的循环算法实现
BSTNode *SearchBST1(BSTNode *bt, KeyType k)
{
while (bt!=NULL)
{
if(k==bt->key)
return bt;
else if(k<bt->key)
bt=bt->lchild;
else
bt=bt->rchild;
}
return NULL;
}
4、二叉排序树的插入生成。
int InsertBST(BSTNode *&p,KeyType k)
{
if(p==NULL)
{
p=(BSTNode *)malloc(sizeof(BSTNode));
p->key=k;
p->lchild=p->rchild=NULL;
return 1;
}
else if (k==p->key)
return 0;
else if (k<p->key)
return InsertBST(p->lchild,k);
else
return InsertBST(p->rchild,k);
}
5、二叉排序树的删除
int DeleteBST(BSTNode *&bt,KeyType k)
{
if(bt=NULL)
return 0;
else
{
if (k<bt->key)
return DeleteBST(bt->lchild,k);
else if(k>bt->key)
return DeleteBST(bt->rchild,k);
else
{
Delete(bt);
return 1;
}
}
}
void Delete(BSTNode *&p)
{
BSTNode *q;
if(p->rchild==NULL)
{
q=p;
p=p->lchild;
free(q);
}
else id(p->lchild==NULL)
{
q=p;
p=p->lchild;
free(q);
}
else
Delete1(p,p->lchild);
}
void Delete1(BSTNode *p, BSTNode *&r)
{
BSTNode *q;
if(r-rchild!=NULL)
Delete1(p,r->rchild);
else
{
p->key=r->key;
q=r;
r=r->lchild;
free(q);
}
}
排序
1、排序定义
typedef int KeyType; // 定义关键字类型
typedef struct // 记录类型
{
KeyType key; //关键字项
InfoType data; // 其他数据项的类型InfoType
}RecType; // 排序的记录类型定义
2、直接插入排序
void InsertSort(RecType R[],int n)
{
int i,j;
for (i=1,j<n;i++)
{
tmp=R[i];
j=i-1;
while (j>=0&&tmp.key<R[j].key)
{
R[j+1]=R[j];
j--;
}
R[j+1]=tmp;
}
}
3、使用折半查找插入排序
void InsertSort1(RecType R[],int n)
{
int i,j,low,high,mid;
RecType tmp;
for(i=1;i<n;i++)
{
tmp=R[i];
low=0;
high=i-1;
// 用折半查找确定插入位置
while (low<=high)
{
mid=(low+high)/2;
if(tmp.key<R[mid].key)
high=mid-1;
else
low=mid+1;
}
// 顺序移动实施插入
for(j=i-1;j>=high+1;j--)
R[j+1]=R[j];
R[high+1]=tmp;
}
}
4、希尔排序
- 先确定一小于n的整数d1作为第一个增量,把表的全部记录分成d1个组,所有距离为d1的倍数的记录放在同一个组中,在各组内进行直接插入排序。
-
然后取出第二个增量d2(小于d1),重复上述的分组和排序,直至所取增量dt=1(d1>d2>d3..>dt-1>dt),即所有记录放在同一组中进行直接插入排序为止。
void ShellSort(RecType R[],int n) { int i,j,gap,k; RecType tmp; gap=n/2; // 增量置初值 while (gap>0) { // 对所有相隔gap位置的所有元素组进行排序 for (i=gap;i<n;i++) { tmp=R[i]; j=i-gap; while (j>=0 && tmp.key<R[j].key) { P[j+gap]=R[j]; j=j-gap; } R[j+gap]=tmp; j=j-gap; } gap=gap/2; //减小增量 } }
5、冒泡排序
- 通过无序区中相邻记录关键字间额比较和位置交换,使关键字最小的记录如气泡一般逐渐往上“漂浮”直至“水面”。
- n个元素排序,n-1趟冒泡。有序
void BubbleSort(RecType R[],int n)
{
int i,j ,k;
RecType tmp;
for(i=0;i<n-1;i++)
{
for(j=n-1;j>i;j--) // 一趟冒泡
if(R[j].key < R[j-1].key)
{
tmp=R[j];
R[j]=R[j-1];
R[j-1]=tmp;
}
}
}
6、快速排序
void QuickSort(RecType R[],int t,int s)
{
int i=s, j=t;
RecType tmp;
if(s<t)
{
tmp=R[s]; //记录基准
// 进行一次划分
while(i!=j)
{
while(j>1&& R[j].key>=tmp.key)
j--;
R[i]=R[j];
while (i<j && R[i].key<=tmp.key)
i++;
R[j]=R[i];
}
R[i]=tmp;
QuickSort(R,s,i-1);
QuickSort(R,i+1,t);
}
}
7、直接选择排序 插入排序
void SelectSort(RecType R[],int n)
{
int i,j,k,l;
RecType temp;
for (i=0;i<n-1;i++)
{
k=i; // 记录无序区的起始位置
// 在无序区选择关键字最小的记录,并记录下标
for (i=i+1;j<n;j++)
if(R[j].key<R[k].key)
k=j;
// 将关键字最小的记录交换到无序区开始
if(k!=i)
{
temp=R[i];
Rp[i]=R[k];
R[k]=temp;
}
}
}
8、堆排序
// 构造初始堆
void sift(RecType R[],int low,int high)
{
int i=low, j=2*i; // R[j] 是R [i] 的左孩子
RecType temp=R[i];
while(j<=high)
{
if (j<high && R[j].key <R[j+1].key)
i++; // 若右孩子较大, j指向右孩 2i+1
if(temp.key<R[j].key)
{
R[i]=R[j]; // 将R[j]调用到双亲节点位置上
i=j; // 修改i和j的值,以便继续向下筛选
j=2*i;
}
else break; // 筛选结束
}
R[i]=temp; // 被筛选节点的值放入最终位置
}
// 进行堆排序
void HeapSort(RecType R[],int n)
{
int i;
RecType temp;
// 循环建立初始堆
for (i=n/2;i>=1; i--)
sift(R,i,n);
// 选最大值置后并调整堆
for (i=n;i>=2;i--)
{
temp=R[1];
R[1]=R[i];
R[i]=temp;
sift(R,1,i-1);
}
}
9、归并排序
-
将两个或两个以上的有序表合并成一个新的有序表
void Merge(RecType R[],int low,int mid,int high) { RecType *R1; int i=low, j=mid+1,k=0; // 动态分配空间R1,用于保存合并结果 R1=(RecType *)malloc(high-low+1)*size(RecType)); // 两段均未扫描时合并 while (i<=mid && 1<=high) if(R[i].key<=R[j].key) { R1[k]=R[i]; i++; k++; } else { R1[k]=R[j]; j++; k++; } // 将第一段余下的部分复制到R1 while(i<=mid) { R1[k]=R[i]; i++; k++; } // 将第二段余下的部分复制到R1 while(j<=high) { R1[k]=R[j]; i++; k++; } // 将合并后的结果复制回R for(k=0,i=low;i<=high;k++,i++) R[i]=R1[k]; } void MergePass(RecType R[],int length,int n) { int i; for (i=0;i+2*length-1<n; i=i+2*length) Merge(R,i,i+length-1,i+2*length-1); if(i+length-1<n) Merge(R,i,i+length-1,n-1); } void MergeSort(RecType R[],int n) { int length; for (length=1;length<n;length=2*length) MergePass(R,length,n); }
#### 基数排序
1、基数排序的特点:
* 不比较的排序:
- 记录R[i]的关键字R[i].key 是由d位数字组成,即k(d-1)K(d-2)...k(0),每一个数字表示关键字的一位,每一位的值都在0<=k(i)<r 范围内(r=10为基数,表示用十进制)
* 两种基数排序
- 最低位优先(LSD)
- 最高位优先(MSD)
* 通过“分配”和“收集”过程来实现排序(以最低位优先为例)
- 先按最低位的值对记录进行分配、收集
- 在前一趟的基础上,再对高位的值分配和收集,直至最高位,则完成了基数排序的整个过程。
* 基数排序是一种借助于多关键字排序的思想对单关键字排序的方法。
2、基数排序的存储结构和算法
#define MAXF 20 //线性表中的最多元素个数
#define MAXR 10 //基数的最大取值
#define MAXD 8 //关键字位数的最大取值
//排序数据节点类型
typedef struct node
{
char data[MAXD];
struct node next;
} RecType1;
RecType1 p;
// 用于分配和收集的队列
RecType1 head[MAXR],tail[MAXR]
void RadixSort(RecType &p,int r, int d)
{
RecType head[MAXR], tail[MAXR],t;
int i,j,k;
for(i=0;i<=d-1;i++) // 从低位到高位
{
// 初始化各链队首和队尾指针
for(j=0;j<r;j++)
head[j]=tail[j]=NULL;
//分配每一个节点
while(p!=NULL)
{
k=p->data[i]-‘0‘; // 将字符装换为数字
if(head[k]==NULL)
{
head[k]=p;
tail[k]=p;
}
else
{
tail[k]->next=p;
tail[k]=p;
}
p=p->next;
}
p=NULL;
for(j=0;j<r;j++)
if(head[j]!=NULL)
{
if(p==NULL)
{
p=head[j];
t=tail[j];
}
else
{
t->next=head[j];
t->tail[j];
}
}
t->next=NULL;
}
#### 各种排序比较
1、平方阶O(n^2)排序,一般称为加单排序,如直接插入,直接选择和冒泡排序。
2、先行对数阶O(nlog2n)排序,如快速,堆和归并排序。
3、线性阶O(n)排序,如基数排序。
![](http://i2.51cto.com/images/blog/201805/10/7d7426a1fed87e39c5874dd46e207bc1.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)
以上是关于数据结构基础的主要内容,如果未能解决你的问题,请参考以下文章