一、排序算法
1、插入排序
1.1 直接插入排序
cpp
void InsertSort(int A[],int n){
int temp,i,j;
for( i = 1;i<n;i++){ //外循环,每个元素都要进行排序
if(A[i-1]>A[i]){ //前面元素比后面元素大的话
temp = A[i];
for( j = i-1; A[j]>temp && j>=0;j--){
A[j+1] = A[j];
}
}
//该趟排序结束找到temp该插入的位置
A[j + 1] = temp;
}
}
1.2、折半查找排序(直接插入算法的改进)
cpp
void sort(int A[], int n) {
int mid, temp, j;
for (int i = 1; i < n; i++) {
temp = A[i];
int low = 0;
int high = i - 1; // 注意是 i-1
while (low <= high) { // 注意是 <=保证稳定性
mid = (low + high) / 2;
if (A[mid] > temp) { //表示插入元素在左边
high = mid - 1;
} else {
low = mid + 1;
}
}
将low到i-1的元素全部右移
for (j = i - 1; j >= low; j--) {
A[j + 1] = A[j];
}
A[low] = temp;
}
}
1.3、希尔排序
cpp
void ShellSort(int A[], int n) {
int i,temp,j,d;
for(d = n/2;d>=1;d = d/2){ //每次循环的增量
for(i=d;i<n;i++){ //每组循环
temp = A[i];
for(j = i;j>=d && A[j-d] > temp;j-=d){
A[j] = A[j-d];
}
A[j] = temp;
}
}
}
2、交换排序
2.1 冒泡排序
cpp
void Swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
void BubbleSort(int A[], int n) {
int i ,j;
bool flag;
for( i = n-1;i>=0;i--){
flag = false;
for(j = 0;j<i;j++){
if(A[j] < A[j+1]){ //冒小泡 >就是冒大泡,大的在后面
Swap(A[j],A[j+1]);
flag = true;
}
}
if(flag == false){
break;
}
}
}
2.2 快速排序
cpp
int Partition(int A[],int low,int high){
int pivot = A[low];
while(low < high){
while(low < high && A[high] >= pivot){
high --;
}
A[low] = A[high];
while(low < high && A[high] <= pivot){
low++;
}
A[high] = A[low];
}
A[low] = pivot;
return low;
}
void QuickSort(int A[],int low,int high) {
if(low < high){
int pivotos = Partition(A,low,high);
QuickSort(A,low,pivotos-1);
QuickSort(A,pivotos+1,high);
}
}
3、选择排序
3.1 简单选择排序
cpp
void Swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
void SelectSort(int A[], int n) {
for (int i = 0; i < n - 1; i++) { //遍历数组,直到倒数第二个元素。
int min = i; // 记录此轮选择的最小元素的索引
for (int j = i + 1; j < n; j++) { //遍历当前未排序的部分(即从索引i+1到n-1),寻找最小元素。
if (A[j] < A[min]) {
min = j; // 更新最小元素的位置
}
}
// 交换选中的最小元素到排序序列的前端
if (min != i) {
Swap(A[i], A[min]); // 传递数组元素的地址
}
}
}
3.2 堆排序
cpp
//先调整以k为根节点的树成为大根堆
void HeadJust(int A[],int k,int n){
int temp = A[k];
//1.找到该节点的左右孩子中最大的那个
for(int i = 2*k;i<=n;i*=2){
if(i<n && A[i+1] > A[i]){
i++;
}
//2.找到更大的元素后判断最大位置的元素和k位置上的元素大小
if(temp >=A[i]){
break;
} else{
//3.将该位置的元素赋值到k位置上
A[k] = A[i];
k = i; //因为i位置上的元素已经保存到k位置上了因此这里需要将k指向i位置,以该节点为根节点继续调整
}
}
A[k] = temp; //k位置不存在左右孩子因此将元素放到K位置
}
//确定k
void build(int A[],int n){
for(int i = n/2;i>=1;i--){ //因为数组的开始下标是1
HeadJust(A,i,n);
}
}
//堆排序
void sort(int A[],int n){
build(A,n);
for(int i = n;i>1;i--){
swap(A[i],A[1]);
HeadJust(A,1,i-1); //1是最大的元素,和最小的元素交换位置
}
}
4、归并排序
cpp
int* B = (int *)malloc(10 * sizeof(int)); // 辅助数组B
// 合并两个有序数组的函数
void Merge(int A[], int low, int mid, int high) {
int i, j, k;
// 将A中所有元素复制到B中
for (k = low; k <= high; k++) {
B[k] = A[k];
}
// 使用for循环将较小值复制到A中
for (i = low,j = mid + 1, k = i; i <= mid && j <= high; k++) {
if (B[i] <= B[j]) {
A[k] = B[i++];
} else {
A[k] = B[j++];
}
}
// 复制剩余的元素
while (i <= mid) {
A[k++] = B[i++];
}
while (j <= high) {
A[k++] = B[j++];
}
}
// 归并排序函数
void MergeSort(int A[], int low, int high) {
if (low < high) {
int mid = (low + high) / 2;
// 对左半部分归并排序
MergeSort(A, low, mid);
// 对右半部分归并排序
MergeSort(A, mid + 1, high);
// 归并
Merge(A, low, mid, high);
}
}
5、基数排序(略)
二、查找
1、顺序查找和折半查找
1.1 折半查找
cpp
int Binary_Search(int A[], int n, int x) {
int low = 0;
int high = n - 1;
int mid;
while (low <= high) {
mid = (low + high) / 2;
if (A[mid] == x) {
return mid;
} else if (A[mid] > x) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
2、树形查找
2.1 二叉排序树(BST)
二叉排序树非递归算法查找
cpp
BiTree BST_Search(BiTree T,int key){
while(T != NULL && key != T->data){
if(key < T->data){ //去左子树查找
T = T->lchild;
}else{
T = T->rchild;
}
}
return T;
}
二叉排序树的递归查找
cpp
//BST中序遍历是有序的,先序后序都行
BiTree BST_Search(BiTree T,int key){
if(T == NULL){
return NULL;
}
BST_Search(T->lchild,key);
if(T->data == key){
return T;
}
BST_Search(T->rchild,key);
}
二叉排序树的递归插入
cpp
int BST_Insert(BiTree &T,int key){
if(T == NULL){
T = new BiTNode;
T->data = key;
T->rchild = T->lchild = NULL;
return 1;
}else if(key == T->data){
return 0; //存在相同值的节点插入失败
}else if(key < T->data){
return BST_Insert(T->lchild,key);
} else{
return BST_Insert(T->rchild,key);
}
}
二叉排序树的非递归插入
不想写,暂略。
判断给定的二叉树是不是二叉排序树
cpp
int pre = -100; //记忆指针
int BST_Judge(BiTree T){
int L,R;
if(T == NULL){
return 1; //空树也是二叉排序树
}
L = BST_Search(T->lchild,key);
if(L == 0 || T->data < pre){ //若左子树返回0或者前驱大于当前结点
return 0;
}
pre = T->data;
R = BST_Search(T->rchild,key);
return R;
}
如果不好想的话,可以中序遍历放到数组中,然后挨个比较。
cpp
int data[100] = {0}; // 假设数组足够大
int index = 0;
void Mid(BiTree T) {
if (T == NULL) {
return; // 空树也是二叉排序树
}
Mid(T->lchild);
data[index++] = T->data;
Mid(T->rchild);
}
bool Judge_BST() {
for (int i = 0; i < index - 1; i++) {
if (data[i] >= data[i + 1]) {
return false; // 不是二叉排序树
}
}
return true; // 是二叉排序树
}
求二叉排序树中最大值和最小值
cpp
//最大值在最右边
ElemType getMax(BiTree T){
if(T->rchild != NULL){
T = T->rchild;
}
return T->data;
}
//最小值在最左边
ElemType getMin(BiTree T){
if(T->lchild != NULL){
T = T->lchild;
}
return T->data;
}
想不到的话还能用数组。
cpp
// 中序遍历二叉树,并将元素存储到数组中
ElemType data[100] = {0};
int index = -1;
void Mid(BiTree T) {
if (T == NULL) {
return;
} else {
Mid(T->lchild);
data[++index] = T->data;
Mid(T->rchild);
}
}
void getMaxAndMin(){
cout<<"最小值是:"<<data[0]<<" 最大值是:"<<data[index];
}
从大到小输出二叉排序树中值不小于K的元素
还是使用数组。详细过程略 。
2.2 二叉平衡树
判断二叉树是不是二叉平衡树
cpp
//求二叉树高度
int getHight(BiTree T){
int hl,hr;
if(T == NULL){
return 0;
}else{
hl = getHight(T->lchild);
hr = getHight(T->rchild);
if(hl > hr){
return hl+1; //1是当前节点本身高度
}else{
return hr+1;
}
}
}
//判断一颗二叉树是否为平衡二叉树
bool isBlance(BiTree T){
int hight_l; //左子树高度
int hight_r; //右子树高度
if(T == NULL){
return true; //空树也是个平衡二叉树
}else{
hight_l = getHight(T->lchild); //求左子树高度
hight_r = getHight(T->rchild); //求右子树高度
if(abs(hight_l-hight_r) <= 1){
return isBlance(T->lchild) && isBlance(T->rchild);
}else{ //根节点不是二叉树
return false;
}
}
}
三、图
1、图的遍历
1.1 邻接矩阵
邻接矩阵的BFS
下面的BFS算法都适用于有向图和无向图的邻接矩阵
cpp
// 找某个顶点的第一个邻居
int firstNeighbor(Graph G, int v) {
for (int i = 0; i < G.vexsnum; i++) {
if (G.maxarcs[v][i] != maxweight) {
return i;
}
}
return -1; // 没有邻居时返回-1
}
// 找到v1顶点除了邻居v2的其他邻居,返回的是第一个邻居的下标
int nextNeighbor(Graph G, int v1, int v2) {
for (int i = v2 + 1; i < G.vexsnum; i++) {
if (G.maxarcs[v1][i] != maxweight) {
return i;
}
}
return -1; // 没有其他邻居时返回-1
}
// 访问标记数据
bool visited_BFS[100];
// 广度优先遍历算法
void BFS(Graph G, int v) {
visit(G.maxvexs[v]);
visited_BFS[v] = true;
Queue Q;
initQueue(Q);
push(Q, v);
while (Q.front != Q.rear) {
int u;
pop(Q, u);
for (int w = firstNeighbor(G, u); w >= 0; w = nextNeighbor(G, u, w)) { // 找到u顶点的全部的邻居
if (!visited_BFS[w]) {
visited_BFS[w] = true;
visit(G.maxvexs[w]);
push(Q, w);
}
}
}
}
// 广度优先遍历所有连通分量
void BFSTraverse(Graph G){
for(int i = 0; i < G.vexsnum; ++i){
visited_BFS[i] = false;
}
for(int i = 0; i < G.vexsnum; ++i){
if(!visited_BFS[i]){
BFS(G, i); // 传递顶点的索引
}
}
}
邻接矩阵的DFS
同样下面的DFS算法都适用于有向图和无向图的邻接矩阵。
cpp
// 找某个顶点的第一个邻居
int firstNeighbor(Graph G, int v) {
for (int i = 0; i < G.vexsnum; i++) {
if (G.maxarcs[v][i] != maxweight) {
return i;
}
}
return -1; // 没有邻居时返回-1
}
// 找到v1顶点除了邻居v2的其他邻居,返回的是第一个邻居的下标
int nextNeighbor(Graph G, int v1, int v2) {
for (int i = v2 + 1; i < G.vexsnum; i++) {
if (G.maxarcs[v1][i] != maxweight) {
return i;
}
}
return -1; // 没有其他邻居时返回-1
}
//访问标记数组
bool visited_DFS[100];
// 深度优先遍历算法
void DFS(Graph G, int v) {
visited_DFS[v] = true;
visit(G.maxvexs[v]);
for (int w = firstNeighbor(G, v); w != -1; w = nextNeighbor(G, v, w)) {
if (!visited_DFS[w]) {
DFS(G, w);
}
}
}
// 深度优先遍历所有连通分量
void DFSTraverse(Graph G){
for(int i = 0; i < G.vexsnum; i++){
visited_DFS[i] = false;
}
for(int i = 0; i < G.vexsnum; ++i){
if(!visited_DFS[i]){
DFS(G, i); // 传递顶点的索引
}
}
}
1.2 邻接表
邻接表的BFS
cpp
// 找到某个顶点的第一个邻居
int firstNeighbor(Graph G, int v) {
ArcNode *p = G.vertices[v].firstarc;
if (p) return p->adjvex;
return -1; // 没有邻居时返回-1
}
// 找到v1顶点除了邻居v2的其他邻居,返回的是第一个邻居的下标
int nextNeighbor(Graph G, int v, int v2) {
ArcNode *p = G.vertices[v].firstarc;
while (p) {
if (p->adjvex != v2) return p->adjvex;
p = p->nextarc;
}
return -1; // 没有其他邻居时返回-1
}
// 广度优先遍历算法
void BFS(Graph G, int v) {
visit(G.vertices[v].data);
visited[v] = true;
Queue Q;
initQueue(Q);
push(Q, v);
while (Q.front != Q.rear) {
int u;
pop(Q, u);
for (int w = firstNeighbor(G, u); w >= 0; w = nextNeighbor(G, u, w)) {
if (!visited[w]) {
visited[w] = true;
visit(G.vertices[w].data);
push(Q, w);
}
}
}
}
邻接表的DFS
cpp
// 找到某个顶点的第一个邻居
int firstNeighbor(Graph G, int v) {
ArcNode *p = G.vertices[v].firstarc;
if (p) return p->adjvex;
return -1; // 没有邻居时返回-1
}
// 找到v1顶点除了邻居v2的其他邻居,返回的是第一个邻居的下标
int nextNeighbor(Graph G, int v, int v2) {
ArcNode *p = G.vertices[v].firstarc;
while (p) {
if (p->adjvex != v2) return p->adjvex;
p = p->nextarc;
}
return -1; // 没有其他邻居时返回-1
}
void DFS(Graph G, int v) {
visited[v] = true;
visit(G.vertices[v].data);
for (int w = firstNeighbor(G, v); w >= 0; w = nextNeighbor(G, v, w)) {
if (!visited[w]) {
DFS(G, w);
}
}
}
不管是邻接表还是邻接矩阵在实现上都差不多主要是firstNeibor和nextNeibor有点不同。
1.3 习题
判断一个无向图是不是一颗树
条件:无回路连通图或n-1边的连通图
cpp
// 访问标记数组
bool visited[max] = {false}; //访问标记数组
// 深度遍历(DFS)
void DFS(Graph &G, int v, int &vexnum, int &arcnum) {
cout << v << " ";
visited[v] = true;
vexnum++; // 顶点数量++
for (ArcNode* p = G.vertice[v].firstArc; p != NULL; p = p->nextArc) {
if (!visited[p->index]) { // 判断当前p指针指向的节点是不是被访问过
arcnum++; // 边++
DFS(G, p->index, vexnum, arcnum);
}
}
}
// 判断图是不是树
bool isTree(Graph &G, int start) {
int vexnum = 0;
int arcnum = 0;
DFS(G, start, vexnum, arcnum);
return vexnum == G.vexnum && arcnum == G.vexnum - 1;
}
四、树和二叉树
二叉树的先、中、后代码略。
1 二叉树求高度
cpp
//递归实现
int lh = 0;
int rh = 0;
int getHigh(BiTree T){
if(T == NULL){
return 0;
}else{
lh = getHigh(T->lchild);
rh = getHigh(T->rchild);
return 1+(lh>rh?lh:rh);
}
}
//求二叉树的高度(非递归)
int getHigh2(BiTree T){
if( T == NULL){
return 0;
}
int h = 0; //树的高度
//初始化队列
Queue Q;
Q.front= Q.rear = -1;
int p = 0; //工作指针指向该层的最后一个节点,front指针和p指针重合的时候树的高度++
enqueue(Q,T);
BiTree e; //出队接受元素
while(!isEmpty(Q)){ // 队列不空出队,然后将其孩子节点入队
dequeue(Q,e);
visit(e->data);
if(e->lchild != NULL){
enqueue(Q,e->lchild);
}
if(e->rchild != NULL){
enqueue(Q,e->rchild);
}
//判断front和p是不是相等
if(p == Q.front){
p = Q.rear;
h++;
}
}
return h;
}
非递归主要是判断当前是第几层。
2、计算二叉树的全部双分支节点个数
cpp
// 计算二叉树所有双分支结点个数的函数
int DNode(BiTree T) {
if (T == NULL) {
return 0;
} else {
// 判断当前节点是不是存在左右孩子节点
int count = (T->lchild != NULL && T->rchild != NULL) ? 1 : 0;
return count + DNode(T->lchild) + DNode(T->rchild);
}
}
3、求二叉树的叶子节点个数
cpp
//求叶子结点个数
int leavel(BiTree T){
if(T ==NULL){
return 0;
}else{
//判断当前节点的左后孩子是不是空.如果为空的话表示当前节点是个叶子节点
int lcount = (T->lchild == NULL && T->rchild == NULL)?1:0;
return lcount+=leavel(T->lchild)+leavel(T->rchild);
}
}
4、求二叉树全部节点的个数
cpp
int AllNode(BiTree T) {
// 函数出口
if (T == NULL) {
return 0;
} else if (T->lchild == NULL && T->rchild == NULL) {
// 如果节点是叶子节点
return 1;
} else {
// 递归计算左子树和右子树的节点数,然后加1(当前节点)
return 1 + AllNode(T->lchild) + AllNode(T->rchild);
}
}
5、先序遍历二叉树找第K个元素
cpp
// 二叉树先序遍历中第 K 个元素
int n = 0;
void preTree_K(BiTree T, int k) {
if (T == NULL) {
return;
} else {
++n; // 注意这个 n++ 一定是写在递归函数执行之前,要不然不准。
// 判断当前的节点是不是第 k 个节点
if (k == n) {
printf("第%d个节点是%d", k, T->data);
return;
}
preTree_K(T->lchild, k); // 递归遍历左子树
preTree_K(T->rchild, k); // 递归遍历右子树
}
}
6、二叉树交换所有结点的左右子树
cpp
void swap(BiTree T){
if(T == NULL){
return;
}else{
BiTree p;
p = T->lchild;
T->lchild = T->rchild;
T->rchild = p;
swap(T->lchild);
swap(T->rchild);
}
}
7、二叉树中打印值为X结点的所有祖先
cpp
int top = -1;
int p[20];
void allparent(BiTree T, ElemType x) {
if (T == NULL) {
return;
} else {
// 入栈
++top;
p[top] = T->data;
// 先递归左子树
allparent(T->lchild, x);
// 如果当前节点是目标节点,则打印所有祖先
if (T->data == x) {
for (int i = 0; i <= top; i++) { // 注意这里应该是小于等于top,因为数组下标是从0开始的
cout << p[i] << " ";
}
cout << endl;
}
// 再递归右子树
allparent(T->rchild, x);
// 出栈
--top;
}
}
8、给定结点在二叉排序树中的层次
cpp
int level_x(BiTree T, int x) {
int level = 1; // 初始化层次计数器
BiTree p = T; // 初始化工作指针为树的根节点
if (T == NULL) {
return -1; // 如果树为空,返回-1
}
while (p != NULL && p->data != x) { // 当前节点不为空且数据不等于要找的数据时循环
if (x < p->data) { // 如果要找的数据小于当前节点的数据,向左子树移动
p = p->lchild;
} else { // 否则,向右子树移动
p = p->rchild;
}
level++; // 每移动一次,层次计数器加1
}
if (p == NULL) {
return -1; // 如果最终p为NULL,表示x不在树中,返回-1
}
return level; // 返回节点的层次
}
9、判断是否为完全二叉树
cpp
bool is_Complete(BiTree T) {
BiTree t = T;
SqQueue q;
initSqQueue(q); // 队列初始化
if (T == NULL) {
return true; // 如果树为空,返回true
} else {
push(q, t); // 根节点入队
bool meetNull = false; // 用于标记是否遇到空节点
while (!isEmpty(q)) { // 队列不空
pop(q, t); // 根节点出队
if (t == NULL) {
meetNull = true; // 标记遇到了空节点
} else {
if (meetNull) { // 如果已经遇到过空节点,再次遇到非空节点,不是完全二叉树
return false;
}
push(q, t->lchild); // 左右孩子入队
push(q, t->rchild);
}
}
}
return true; // 所有检查通过,是完全二叉树
}
10、逆层序遍历二叉树
cpp
void level(BiTree T) {
if (T == NULL) {
return;
}
SqQueue Q; // 初始化队列
initSqQueue(Q);
SqStack S; // 初始化栈
initSqStack(S);
BiTree e; // 接受出队元素
pushSqQueue(Q, T); // 根节点入队
while (!isEmptySqQueue(Q)) { // 当队列不为空时
popSqQueue(Q, e); // 队头出队
pushSqStack(S, e); // 队头入栈
if (e->lchild) {
pushSqQueue(Q, e->lchild); // 左孩子入队
}
if (e->rchild) {
pushSqQueue(Q, e->rchild); // 右孩子入队
}
}
while (!isEmptySqStack(S)) { // 当栈不为空时
popSqStack(S, e); // 栈顶出栈
printf("%d ", e->data); // 输出节点数据
}
}
11、递归求树的宽度
cpp
// 递归求树宽度
int width[10] = {0}; // 层数
void pre(BiTree T, int level) { // level 标注当前节点是第几层的
if (T == NULL) {
return;
} else {
// 第一次访问节点,当前层数的节点数+1
width[level]++;
pre(T->lchild, level + 1);
pre(T->rchild, level + 1);
}
}
void getWidth(BiTree T) {
int max = 0;
pre(T, 0);
for (int i = 0; i < 10; i++) {
if (max < width[i]) {
max = width[i];
}
}
printf("二叉树的宽度是:%d", max);
}
12、满二叉树已知先序序列,求后序序列
13、已知先序和中序建立二叉链表
cpp
// 定义创建二叉树的函数,接受先序和中序遍历数组及其索引范围
BiTree createTree(int pre[], int mid[], int l1, int h1, int l2, int h2) {
// 如果先序或中序数组的索引范围无效,则返回NULL,表示没有树可以创建
if (l1 > h1 || l2 > h2) {
return NULL;
}
// 先创建根节点
BiTree T = new BiTNode; // 分配新的二叉树节点
T->data = pre[l1]; // 先序遍历的第一个元素是根节点的值
// 在中序遍历中找到根节点的位置
int i;
for (i = l2; i <= h2; i++) {
if (mid[i] == T->data) {
break; // 找到根节点在中序遍历中的位置
}
}
// 计算左子树和右子树的长度
int left_len = i - l2; // 左子树的长度
int right_len = h2 - i; // 右子树的长度
// 递归创建左子树
if (left_len != 0) {
T->lchild = createTree(pre, mid, l1 + 1, l1 + left_len, l2, i - 1); // 先序和中序的左子树范围
} else {
T->lchild = NULL; // 如果没有左子树,则设置为空
}
// 递归创建右子树
if (right_len != 0) {
T->rchild = createTree(pre, mid, l1 + left_len + 1, h1, i + 1, h2); // 先序和中序的右子树范围
} else {
T->rchild = NULL; // 如果没有右子树,则设置为空
}
// 返回创建的二叉树的根节点
return T;
}
14、树与二叉树-以兄弟链表存储,用递归求树的深度
cpp
// 定义计算二叉树高度的函数,接受二叉树的根节点作为参数
int getHight(BiTree T) {
int left; // 左子树的高度
int right; // 右子树的高度
// 如果树为空(即根节点为空),则高度为0
if (T == NULL) {
return 0;
} else {
// 递归计算左子树的高度
left = getHight(T->lchild);
// 递归计算右子树的高度
right = getHight(T->rchild);
}
// 比较左右子树的高度,取较大的高度,并加1(当前节点的高度)
if (left + 1 > right) {
return left + 1; // 如果左子树的高度大于右子树,则返回左子树高度加1
} else {
return right + 1; // 如果右子树的高度大于或等于左子树,则返回右子树高度加1
}
}
15、将给定的表达式树转中缀表达式
cpp
// 将给定的表达式树转换为中缀表达式
void convert(BiTree T, int deep) {
// 如果当前节点为空,直接返回
if (T == NULL) {
return;
}
// 如果当前节点是叶子节点(没有左右子节点)
else if (T->lchild == NULL && T->rchild == NULL) {
// 输出叶子节点的数据
cout << T->data;
}
// 如果当前节点不是叶子节点
else {
// 如果当前节点的深度大于1,输出左括号
if (deep > 1) {
cout << "(";
}
// 递归转换左子树,深度加1
convert(T->lchild, deep + 1);
// 输出当前节点的数据
cout << T->data;
// 递归转换右子树,深度加1
convert(T->rchild, deep + 1);
// 如果当前节点的深度大于1,输出右括号
if (deep > 1) {
cout << ")";
}
}
}
两层加括号
16、中序线索二叉树找后序前驱
17、将叶节点按从左到右的顺序连成单链表
cpp
// 将二叉树的叶节点按从左到右的顺序链接成单链表
void Link(BiTree T, BiTree& Head, BiTree& tail) {
// 如果当前节点不为空
if (T) {
// 如果当前节点是叶子节点(既没有左孩子也没有右孩子)
if (T->lchild == NULL && T->rchild == NULL) {
// 如果链表为空(还没有头节点)
if (Head == NULL) {
Head = T; // 当前节点成为链表的头节点
tail = T; // 当前节点也成为链表的尾节点
} else { // 如果链表不为空
tail->rchild = T; // 将当前节点链接到链表的尾节点
tail = T; // 更新尾节点为当前节点
}
}
// 递归处理左子树
Link(T->lchild, Head, tail);
// 递归处理右子树
Link(T->rchild, Head, tail);
}
}
18、求WPL
cpp
// 初始化加权路径长度为0
int wpl = 0;
// 定义一个函数preTree,用于计算二叉树的加权路径长度(WPL)
int preTree(BiTree T, int dep) {
// 如果当前节点为空,返回0,不增加路径长度
if (T == NULL) {
return 0;
} else {
// 如果当前节点是叶子节点
if (T->lchild == NULL && T->rchild == NULL) {
// 计算当前叶子节点的加权路径长度,并累加到wpl
// 这里假设T->data存储的是字符形式的数字,需要转换为整数
wpl += (dep - 1) * (T->data);
}
// 如果当前节点不是叶子节点,则递归计算左右子树的WPL
else {
preTree(T->lchild, dep + 1); // 递归左子树,深度加1
preTree(T->rchild, dep + 1); // 递归右子树,深度加1
}
}
// 返回当前节点的加权路径长度
return wpl;
}
19、删除树中以x为根节点的子树,并释放空间
cpp
// 删除二叉树节点,并释放其占用的内存空间
void del(BiTree &T) {
// 如果当前节点为空,直接返回,不进行任何操作
if (T == NULL) {
return;
} else {
// 递归删除当前节点的左子树
del(T->lchild);
// 递归删除当前节点的右子树
del(T->rchild);
// 释放当前节点的内存
free(T);
// 将当前节点指针设置为NULL,避免野指针
T = NULL;
}
}
// 删除二叉树中所有以x为根节点的子树
void del_allx(BiTree &T, int x) {
// 如果当前节点为空,直接返回,不进行任何操作
if (T == NULL) {
return;
} else {
// 判断当前节点是否是要删除的节点
if (T->data == x) {
// 如果是,递归调用del函数删除该节点及其所有子节点
del(T);
// 删除完成后返回,不再继续遍历
return;
}
// 递归检查左子树中是否包含要删除的节点
del_allx(T->lchild, x);
// 递归检查右子树中是否包含要删除的节点
del_allx(T->rchild, x);
}
}
20、寻找两个节点的公共祖先
cpp
// 定义一个函数findLCA,用于在二叉树中寻找两个节点p和q的最近公共祖先
BiTree findLCA(BiTree root, ElemType p, ElemType q) {
// 如果当前节点为空,或者当前节点就是p或q,那么返回当前节点
if (root == NULL || root->data == p || root->data == q) {
return root;
}
// 递归在左子树中查找p和q的最近公共祖先
BiTree left = findLCA(root->lchild, p, q);
// 递归在右子树中查找p和q的最近公共祖先
BiTree right = findLCA(root->rchild, p, q);
// 如果在左右子树中都找到了目标节点,说明当前节点是p和q的最近公共祖先
if (left != NULL && right != NULL) {
return root;
}
// 如果只在一边找到了目标节点,那么返回找到的那一边的最近公共祖先
// left != NULL ? left : right 是一个三元运算符,如果left不为空则返回left,否则返回right
return left != NULL ? left : right;
}
想不到的话还有笨方法。
cpp
BiTree data[maxsize] = {NULL}; // 初始化一个数组,用于存储二叉树的节点,初始值设为NULL
int index = 0; // 用于数组data的下标,表示当前存储的节点位置
// 定义一个函数levelOrder,用于进行二叉树的层序遍历,并找出两个节点p和q的最近公共祖先
void levelOrder(BiTree T, ElemType p, ElemType q) {
// 初始化队列Q
Queue Q;
Q.front = Q.rear = 0;
int _p, _q; // 用于存储p和q节点在数组data中的下标
// 如果根节点不为空,则将其入队
if (T != NULL) {
enqueue(Q, T);
}
// 当队列不为空时,进行循环
while (!isEmpty(Q)) {
BiTree node;
// 从队列中出队一个节点
dequeue(Q, node);
// 将出队的节点存储到数组data中,并更新下标index
data[++index] = node;
// 如果当前节点的数据等于p,则记录p的下标
if (node->data == p) {
_p = index;
}
// 如果当前节点的数据等于q,则记录q的下标
if (node->data == q) {
_q = index;
}
// 如果当前节点不为空,则访问该节点,并将其左右孩子入队(如果它们不为空)
if (node != NULL) {
visit(node->data); // 访问该节点,例如打印节点数据
if (node->lchild != NULL) {
enqueue(Q, node->lchild);
}
if (node->rchild != NULL) {
enqueue(Q, node->rchild);
}
}
}
// 通过下标计算p和q的最近公共祖先的下标
while (_p != _q) {
_p /= 2; // _p除以2,相当于在层序遍历的数组中向上移动一层
_q /= 2; // _q除以2,同理
}
// 输出p和q的最近公共祖先的节点数据
cout << p << "和" << q << "元素的祖先是:" << data[_p]->data << endl;
}
五、串的基本操作
结构体
cpp
typedef struct str { // 顺序串的结构体
char ch[MAXSIZE + 1];
int length;
} str;
1、赋值操作
cpp
// 定义一个函数StrAssign,用于将字符数组chs的值赋给串S
void StrAssign(str &S, char chs[]) {
int i = 0; // 初始化索引变量i,用于遍历字符数组chs
// 循环遍历字符数组chs,直到遇到字符串结束符'\0'
while (chs[i] != '\0') {
// 将字符数组chs中的字符逐个赋值给串S的数据成员ch
S.ch[i] = chs[i];
// 索引i自增,移动到字符数组的下一个字符
i++;
}
// 当循环结束时,索引i等于字符数组chs的长度
// 将计算出的长度赋值给串S的长度成员length
S.length = i;
}
2、复制操作
cpp
// 定义一个函数StrCopy,用于将串T的值复制到串S中
void StrCopy(str &S, str &T) {
// 使用for循环遍历串T中的每个字符
for (int i = 0; i < T.length; i++) {
// 将串T中的字符逐个复制到串S的对应位置
S.ch[i] = T.ch[i];
}
// 在串S的字符数组的末尾添加字符串结束符'\0',确保字符串正确结束
S.ch[T.length] = '\0';
// 将串T的长度复制到串S的长度成员中
S.length = T.length;
}
3、判断字符串是不是空串
cpp
// 判断字符串是不是空串
bool IsStrEmpty( str &S) {
return S.length == 0;
}
4、比较操作
cpp
// 定义一个函数StrCompare,用于比较两个字符串S和T的大小
int StrCompare(str &S, str &T) {
// 循环比较两个字符串中对应位置的字符
for (int i = 0; i < S.length && i < T.length; i++) {
// 如果S中的字符ASCII值大于T中的字符ASCII值,返回1
if (S.ch[i] > T.ch[i]) return 1;
// 如果S中的字符ASCII值小于T中的字符ASCII值,返回-1
if (S.ch[i] < T.ch[i]) return -1;
}
// 如果S的长度大于T的长度,返回1
if (S.length > T.length) return 1;
// 如果S的长度小于T的长度,返回-1
if (S.length < T.length) return -1;
// 如果上述条件都不满足,说明两个字符串相等,返回0
return 0;
}
5、求串长
cpp
//求字符串的长度
int StrLength( str S) {
return S.length;
}
6、求子串
cpp
// 定义一个函数SubSting,用于从字符串S中提取从位置pos开始的长度为len的子串,并存储到Sub中
void SubSting(str &Sub, str S, int pos, int len) {
// 首先检查字符串S是否为空
if (IsStrEmpty(S)) {
return;
}
// 检查pos和len是否合理,即pos是否在字符串范围内,len是否非负,以及子串是否会超出原字符串的范围
if (pos < 1 || pos > S.length || len < 0 || (pos + len - 1 > S.length)) {
cout << "输入有误!" << endl; // 如果输入有误,输出错误信息
return;
}
int i, j; // 定义两个索引变量i和j,i用于遍历原字符串S,j用于遍历子串Sub
// 循环从S中提取子串,i从pos-1开始(因为数组索引从0开始),j从0开始
for (i = pos - 1, j = 0; i < pos + len - 1 && i < S.length; i++, j++) {
Sub.ch[j] = S.ch[i]; // 将S中的字符赋值给Sub
}
Sub.ch[j] = '\0'; // 在Sub的末尾添加字符串结束符'\0',确保子串正确结束
Sub.length = j; // 更新Sub的长度为实际提取的字符数
}
7、串连接
cpp
// 定义一个函数Concat,用于将两个字符串S3和S4连接起来,并将结果存储在T1中
void Concat(str &T1, str &S3, str &S4) {
// 如果S3和S4都是空串,那么T1也将是一个空串
if (IsStrEmpty(S3) && IsStrEmpty(S4)) {
T1.length = 0; // 设置T1的长度为0
T1.ch[0] = '\0'; // 确保空串以'\0'结尾
return; // 结束函数执行
}
int i; // 定义索引变量i,用于遍历S3
// 将S3中的字符复制到T1中
for (i = 0; i < S3.length; i++) {
T1.ch[i] = S3.ch[i];
}
int j; // 定义索引变量j,用于遍历S4
// 将S4中的字符复制到T1中,紧接在S3的字符之后
for (j = 0; j < S4.length; j++, i++) { // j从0开始,i从S3.length开始
T1.ch[i] = S4.ch[j];
}
T1.ch[i] = '\0'; // 在T1的末尾添加字符串结束符'\0',确保连接后的串正确结束
T1.length = i; // 更新T1的长度,i此时是连接后字符串的最后一个字符的索引
}
8、定位
cpp
// 定义一个函数Index,用于在字符串S5中查找子串S6的起始位置
int Index(str &S5, str &S6) {
// 如果S6是空串或者S5的长度小于S6的长度,则返回0或-1
if (IsStrEmpty(S6) || S5.length < S6.length) {
return -1; // 返回-1表示子串不在主串中
}
int i = 0, j = 0; // i用于遍历S5,j用于遍历S6
// 使用while循环进行查找
while (i < S5.length && j < S6.length) {
// 如果S5和S6在当前位置的字符相同,则两个索引都向前移动
if (S5.ch[i] == S6.ch[j]) {
i++;
j++;
} else {
// 如果字符不匹配,i保持不变,j重置为0,重新开始匹配S6的起始字符
j = 0;
}
}
// 如果j等于S6的长度,说明S6已经在S5中完全匹配
if (j == S6.length) {
return i - j; // 返回匹配的起始位置,注意这里的返回值应该是i - j,因为i和j在最后一次匹配时是同步的
} else {
// 如果S6没有在S5中找到,返回-1
return -1;
}
}
六、栈、队列和数组
1、链式栈(带头)
cpp
#include<stdio.h>
#include<stdlib.h>
#include <iostream>
#include <cstdlib>
using namespace std;
typedef struct LinkNode{
int data; // 栈顶元素
struct LinkNode* next; // 栈顶指针
}LinkNode,*LinkStack;
// 链栈初始化(带头节点)
LinkStack Init(){
// 创建头节点
LinkStack S = (LinkStack) malloc(sizeof(LinkNode));
S->data = 0;
S->next = NULL;
return S; // 返回头节点
}
// 判断链式栈是不是空
bool isEmpty(LinkStack S){
if(S->next == NULL){
return true;
}
return false;
}
// 链栈入栈
void push(LinkStack &S, int data){
// 创建新节点
LinkStack p = (LinkStack)malloc(sizeof(LinkNode));
p->data = data;
p->next = S->next;
S->next = p;
}
// 链式栈出栈
void pop(LinkStack &S){
// 链式栈不需要沾满判断
LinkStack p = S->next;
if(p==NULL){
cout<<"当前栈为空"<<endl;
}else{
cout<<"栈顶元素出栈:"<<p->data<<endl;
S->next = p->next;
free(p);
}
}
//链式栈取栈顶元素
int getTop(LinkStack &S){
LinkStack p = S->next;
if(isEmpty(S)){
cout<<"链式栈为空"<<endl;
}else{
return p->data;
}
}
//链式栈求栈的长度(元素的个数)
int getCount(LinkStack S){
int count = 0;
LinkStack p = S->next;
while(p!=NULL){
count++;
p = p->next;
}
return count;
}
int main(){
LinkStack S;
S = Init(); // 初始化链栈并接收返回值
push(S, 2);
push(S, 3);
push(S, 9);
push(S, 7);
// while(!isEmpty(S)) pop(S); // 循环直到栈为空
cout<<"栈顶元素是:"<<getTop(S)<<endl;
cout<<"栈中元素个数:"<<getCount(S)<<endl;
return 0;
}
2、链式栈(无头)
cpp
#include<stdio.h>
#include<stdlib.h>
#include <iostream>
#include <cstdlib>
using namespace std;
typedef struct LinkNode{
int data; // 栈顶元素
struct LinkNode* next; // 栈顶指针
}LinkNode,*LinkStack;
// 链栈初始化(带头节点)
void Init(LinkStack &S){
S = NULL;
}
// 判断链式栈是不是空
bool isEmpty(LinkStack S){
if(S == NULL){
return true;
}
return false;
}
// 链栈入栈
void push(LinkStack &S, int data){
// 创建新节点
LinkStack p = (LinkStack)malloc(sizeof(LinkNode));
p->data = data;
p->next = S;
S = p;
}
// 链式栈出栈
void pop(LinkStack &S){
// 链式栈不需要沾满判断
LinkStack p = S;
if(p==NULL){
cout<<"当前栈为空"<<endl;
}else{
cout<<"栈顶元素出栈:"<<p->data<<endl;
S = p->next;
free(p);
}
}
//链式栈取栈顶元素
int getTop(LinkStack &S){
LinkStack p = S;
if(isEmpty(S)){
cout<<"链式栈为空"<<endl;
}else{
return p->data;
}
}
//链式栈求栈的长度(元素的个数)
int getCount(LinkStack S){
int count = 0;
LinkStack p = S;
while(p!=NULL){
count++;
p = p->next;
}
return count;
}
int main(){
LinkStack S;
Init(S); // 初始化链栈并接收返回值
push(S, 2);
push(S, 3);
push(S, 9);
push(S, 7);
// while(!isEmpty(S)) pop(S); // 循环直到栈为空
cout<<"栈顶元素是:"<<getTop(S)<<endl;
cout<<"栈中元素个数:"<<getCount(S)<<endl;
return 0;
}
3、链式队列
cpp
#include <iostream>
#include <cstdlib>
using namespace std;
// 定义链式队列的节点结构体
typedef struct QueueNode {
int data;
struct QueueNode* next;
} QueueNode;
// 定义链式队列的类型
typedef struct {
QueueNode* front; // 队列头指针
QueueNode* rear; // 队列尾指针
} LinkQueue;
// 初始化链式队列
void init(LinkQueue* L) {
L->front = L->rear = (QueueNode*)malloc(sizeof(QueueNode));
if (!L->front) {
cout << "内存分配失败!" << endl;
exit(1);
}
L->front->next = NULL;
}
// 判断链式队列是否为空
bool isEmpty(LinkQueue L) {
return L.front == L.rear;
}
// 入队操作
void push(LinkQueue* L, int data) {
QueueNode* p = (QueueNode*)malloc(sizeof(QueueNode));
if (!p) {
cout << "内存分配失败!" << endl;
exit(1);
}
p->data = data;
p->next = NULL;
L->rear->next = p;
L->rear = p;
}
// 出队操作
bool pop(LinkQueue* L, int& data) {
if (isEmpty(*L)) {
cout << "队列为空,无法出队!" << endl;
return false;
}
QueueNode* temp = L->front->next;
data = temp->data;
L->front->next = temp->next;
if (L->rear == temp) { // 如果移除的是最后一个节点,更新rear指针
L->rear = L->front;
}
free(temp);
return true;
}
// 销毁链式队列,释放内存
void destroy(LinkQueue* L) {
while (!isEmpty(*L)) {
pop(L, data);
}
free(L->front);
L->front = L->rear = NULL;
}
// 主函数,用于演示链式队列的操作
int main() {
LinkQueue L;
init(&L);
push(&L, 1);
push(&L, 2);
push(&L, 3);
int data;
while (pop(&L, data)) {
cout << "出队元素: " << data << endl;
}
destroy(&L);
return 0;
}
七、线性表
1、逆转顺序表中的所有元素
cpp
// 函数reserve用于将数组A的元素逆序存储到数组B中,并打印结果
void reserve(int A[], int n) {
int i, j;
int B[n]; // 定义辅助数组B,大小与A相同
// 逆序复制A中的元素到B中
for (i = n - 1; i >= 0; i--) {
B[n - 1 - i] = A[i];
}
// 打印数组B中的元素
for (j = 0; j < n; j++) {
printf("%d ", B[j]);
}
printf("\n"); // 打印换行符,使输出更清晰
}
2、删除给定值的元素
cpp
// 删除顺序表L中值为e的元素
int del(SqList &L, int e) {
// 判断当前的顺序表是不是空的
if (L.len <= 0) {
return 0; // 如果顺序表为空,直接返回0
}
// 遍历顺序表
for (int i = 0; i < L.len; i++) { // 注意这里的循环条件应该是 i < L.len
// 如果找到值为e的元素
if (e == L.elem[i]) {
// 将找到的元素后面的所有元素向前移动一位,覆盖要删除的元素
for (int j = i + 1; j < L.len; j++) {
L.elem[j - 1] = L.elem[j];
}
L.len--; // 顺序表长度减1
return 1; // 删除成功,返回1
}
}
return 0; // 如果没有找到值为e的元素,返回0
}
3、删除给定范围值的元素
cpp
// 删除顺序表L中,值在给定范围s到t之间的所有元素(假设t > s)
void del(SqList &L, int s, int t) {
// 判断顺序表是否为空
if (L.length <= 0) {
printf("不存在元素");
return;
}
// 检查s和t的值是否合法,即t是否大于s
if (!(t > s)) {
printf("元素位置不合法");
return;
}
int j = 0; // j用于记录新顺序表的长度
// 遍历顺序表
for (int i = 0; i < L.length; i++) {
// 判断当前元素是否不在s到t的范围内
if (!(L.elem[i] >= s && L.elem[i] <= t)) {
L.elem[j] = L.elem[i]; // 将不在范围内的元素复制到前面
j++; // 新顺序表长度加1
}
}
L.length = j; // 更新顺序表的长度为新长度
}
4、从有序顺序表中删除其值重复的元素
cpp
// 删除有序顺序表中重复的元素
void del(SqList &L) {
// 健壮性检查:如果顺序表长度小于等于0,直接返回
if (L.length <= 0) {
return;
}
int count = 0; // 记录出现重复元素的次数
// 从第二个元素开始遍历顺序表
for (int i = 1; i < L.length; i++) {
// 如果当前元素与前一个元素相等,说明是重复元素
if (L.elem[i] == L.elem[i - 1]) {
count++; // 重复元素计数加1
} else {
// 如果当前元素与前一个元素不相等,将当前元素复制到前一个元素的位置
L.elem[i - count] = L.elem[i];
}
}
// 更新顺序表的长度,减去重复元素的数量
L.length -= count;
}
5、带头结点的单链表"就地"逆置。
cpp
// 带头节点的单链表就地反转
LinkList reserve(LinkList &L) {
LinkList beg = L->next; // 指向原链表的第一个有数据的节点
LinkList end = L->next->next; // 指向原链表的第二个有数据的节点
// 当beg的下一个节点不为空时,进行反转操作
while (beg->next != NULL) {
beg->next = end->next; // 将beg的下一个节点指向end的下一个节点,实现反转
end->next = L->next; // 将end的下一个节点指向头节点,更新end的next为原头节点
L->next = end; // 更新头节点的next为end,即新的头节点
end = beg->next; // 更新end为新的节点,准备下一轮反转
}
// 反转完成后,返回新的头节点
return L->next;
}

6、按序号奇偶,拆分单链表
cpp
// 定义一个函数,用于将链表L中的节点按照奇数和偶数位置分离到两个新链表中
void aaa(LinkList &L) {
LinkList p = L->next; // 指向链表的第一个数据节点
// 创建两个新的头节点,用于构建奇数和偶数链表
LinkList headA = (LinkList)malloc(sizeof(LNode)); // 奇数链表的头节点
LinkList headB = (LinkList)malloc(sizeof(LNode)); // 偶数链表的头节点
LinkList Aa = headA; // 用于构建奇数链表的当前节点
LinkList Bb = headB; // 用于构建偶数链表的当前节点
headA->next = NULL; // 初始化奇数链表的头节点的next为NULL
headB->next = NULL; // 初始化偶数链表的头节点的next为NULL
int index = 1; // 用于记录当前节点的位置,1表示第一个节点,即奇数位置
// 当p不为空,即链表未遍历完
while (p != NULL) {
if (index % 2 == 1) { // 如果当前是奇数个节点
Aa->next = p; // 将当前节点p链接到奇数链表的末尾
Aa = Aa->next; // 移动Aa指针到奇数链表的末尾
L->next = p->next; // 将原链表的当前节点的next移动到下一个节点
p->next = NULL; // 将当前节点p的next设置为NULL,断开与原链表的连接
p = L->next; // 移动p指针到原链表的下一个节点
} else { // 如果当前是偶数个节点
Bb->next = p; // 将当前节点p链接到偶数链表的末尾
Bb = Bb->next; // 移动Bb指针到偶数链表的末尾
L->next = p->next; // 将原链表的当前节点的next移动到下一个节点
p->next = NULL; // 将当前节点p的next设置为NULL,断开与原链表的连接
p = L->next; // 移动p指针到原链表的下一个节点
}
index++; // 节点位置计数加1
}
// 打印奇数序列
printf("奇数序列:\n");
printList(headA); // 假设printList是一个打印链表的函数
printf("\n");
// 打印偶数序列
printf("偶数序列:\n");
printList(headB); // 假设printList是一个打印链表的函数
}
7、链表中的节点交替逆序排列
cpp
// 定义一个函数,用于对单链表L进行重排序
void reSort(LinkList &L) {
// 初始化指针p和q,p走一步,q走两步
LinkList p = L->next;
LinkList q = L->next;
LinkList r;
// 当q未走到链表尾部时,继续循环
while (q->next != NULL) {
p = p->next; // p走一步
q = q->next; // q走一步
// 如果q还有下一个节点,q再走一步
if (q->next != NULL) {
q = q->next;
}
}
// 此时,q指向链表的尾部,p指向链表的中间位置
// 创建一个新节点作为重排序后链表的头节点
q = p->next;
p->next = NULL; // 断开原链表,准备重排序
// 使用前插法逆置q指向的链表后半段
while (q != NULL) {
r = q->next; // 保存q的下一个节点
q->next = p->next; // 将q的下一个节点指向p的下一个节点
p->next = q; // 将p的下一个节点指向q
q = r; // q移动到下一个节点
}
// 将逆置后的后半段链表与前半段链表合并
LinkList s = L->next;
p->next = q; // 将前半段链表的末尾节点指向逆置后的后半段链表的头节点
p->next = NULL; // 重置p的next为NULL,准备合并前半段链表
while (q != NULL) {
r = q->next; // 保存q的下一个节点
q->next = s->next; // 将q的下一个节点指向s的下一个节点
s->next = q; // 将s的下一个节点指向q
s = q->next; // s移动到下一个节点
q = r; // q移动到下一个节点
}
// 打印重排序后的链表
printList(L);
}
8、单链表找倒数第k个位置上的结点
cpp
// 定义一个函数Search_K,用于在单链表L中查找倒数第k个元素
int Search_K(LinkList L, int k) {
LinkList p = L->next, q = L->next; // 初始化两个指针p和q,都指向链表的第一个数据节点
int count = 0; // 用于记录步数
// 当p不为空时,继续循环
while (p != NULL) {
if (count < k) {
count++; // 增加步数
p = p->next; // p先走k步,使得p和q之间相差k个元素
} else {
q = q->next; // p走完k步后,q开始移动,与p一起往后走
p = p->next;
}
}
// 如果count小于k,说明在走到k步之前链表已经结束,不存在倒数第k个节点
if (count < k) {
return 0; // 返回0表示未找到
} else {
// 打印倒数第k个元素的数据
printf("倒数第%d个元素是%d", k, q->data);
return 1; // 返回1表示找到
}
}
9、单链表,删除绝对值相等的结点,只保留第一次出现的
cpp
// 定义一个函数aaa,用于删除单链表L中的重复元素
void aaa(LinkList &L) {
// 创建辅助数组,用于标记元素是否已经出现过
int fuzhu[20] = {0}; // 默认数组中每个元素的值是0,表示元素未出现过
// 初始化指针p指向链表的头节点
LinkList p = L;
LinkList r = NULL; // 指向要删除的节点
int index; // 数组下标
// 循环遍历链表,直到p的下一个节点为空
while (p->next != NULL) {
// 取出data转为正数,用于作为辅助数组的下标
index = (p->next->data < 0) ? (-(p->next->data)) : (p->next->data);
// 如果该元素未在辅助数组中标记为出现过
if (fuzhu[index] == 0) {
fuzhu[index] = 1; // 标记该元素已出现
p = p->next; // 移动p到下一个节点
} else { // 如果该元素已经出现过,即第二次出现
r = p->next; // 保存要删除的节点
p->next = r->next; // 删除元素,将p的next指向r的下一个节点
free(r); // 释放要删除节点的内存
}
}
// 打印单链表,显示删除重复元素后的结果
printList(L);
}
10、按递增顺序输出单链表元素,并删除最小元素
cpp
// 定义一个函数minDel,用于按递增顺序输出单链表L的元素,并删除最小元素
void minDel(LinkList &L) {
LinkList pre = NULL; // pre指向被删除元素的前驱节点
LinkList p = NULL; // 工作指针,用于遍历链表
LinkList q = NULL; // 指向待删除的节点
// 当链表不为空时,执行循环
while (L->next != NULL) {
pre = L; // pre初始化指向链表的头节点
p = pre->next; // p初始化指向链表的第一个数据节点
// 遍历链表,寻找最小元素
while (p->next != NULL) {
// 如果找到更小的元素,更新pre指向更小元素的前驱节点
if (p->next->data < pre->next->data) {
pre = p;
}
p = p->next; // 移动p指针到下一个节点
}
// 输出最小元素
printf("本次最小的节点是: %d\n", pre->next->data);
// 删除最小元素
q = pre->next; // q指向最小元素
pre->next = q->next; // 从链表中删除最小元素
free(q); // 释放最小元素节点的内存
// 输出链表,显示删除最小元素后的结果
printList(L);
printf("\n"); // 打印换行符,为下一次输出做准备
}
}
11、递归删除不带头结点单链表中值为x的结点
cpp
// 定义一个函数Del_x,用于在单链表L中删除值为x的所有节点
void Del_x(LinkList &L, int x) {
LinkList p; // 定义一个指针p,用于指向要删除的节点
// 如果链表为空,直接返回
if (L == NULL) {
return;
}
// 如果当前节点的数据等于x
if (L->data == x) {
p = L; // 将p指向要删除的节点
L = L->next; // 将头指针L移动到下一个节点,绕过要删除的节点
free(p); // 释放要删除节点的内存
Del_x(L, x); // 递归调用Del_x,继续在剩余链表中查找并删除值为x的节点
} else {
// 如果当前节点的数据不等于x,递归调用Del_x,继续在下一个节点中查找
Del_x(L->next, x);
}
}
12、给定两个升序单链表 ,将其合并后仍为升序并输出。
cpp
// 假设L1、L2有头节点
void resort(LinkList &L1, LinkList &L2) {
// p1指向L1的第一个数据节点
LinkList p1 = L1->next;
// p2指向L2的第一个数据节点
LinkList p2 = L2->next;
// q1和q2用于临时存储节点的下一个节点
LinkList q1, q2;
// 遍历L2,直到p2为NULL,即L2的末尾
while(p2 != NULL) {
// 如果p2指向的节点的数据大于等于p1指向的节点的数据
if(p2->data >= p1->data) {
// 保存p1的下一个节点
q1 = p1->next;
// 将p2插入到p1的前面
p1->next = p2;
// 保存p2的下一个节点
q2 = p2->next;
// 将p2的next指向q1,即p1原来指向的节点
p2->next = q1;
}
// p1移动到下一个节点
p1 = q1;
// p2移动到下一个节点
p2 = q2;
}
}
13、顺序表逆置
cpp
void reserve(int A[], int n) {
int i, temp; // 定义循环变量i和临时变量temp用于交换数组元素
// 遍历数组的前半部分,将每个元素与其对应的后半部分元素交换
// 循环条件修正为i < n / 2,确保不会越界
for(i = 0; i < n / 2; i++) {
temp = A[i]; // 保存当前元素的值
A[i] = A[n - i - 1]; // 将当前元素与对应的后半部分元素交换
A[n - i - 1] = temp; // 将后半部分元素的值赋给当前位置
}
// 遍历数组并打印每个元素,展示数组反转后的结果
for(i = 0; i < n; i++) {
printf("%d ", A[i]); // 打印当前元素
}
}
14、删除顺序表中所有值为x的元素
cpp
// 删除顺序表中全部的值为x的元素,时间复杂度为n
void del_x(SqList &L, ElemType e) {
if (L.length == 0) {
cout << "顺序表为空" << endl; // 如果顺序表为空,则输出提示信息并返回
return;
}
int k = 0; // k用于记录不为要删除元素的位置
// 遍历顺序表中的每个元素
for (int i = 0; i < L.length; i++) {
if (L.data[i] != e) {
L.data[k] = L.data[i]; // 如果当前元素不等于要删除的元素,则将其复制到k的位置
k++; // 增加k的值,指向下一个可能的非删除元素的位置
}
}
L.length = k; // 更新顺序表的长度,排除所有被删除的元素
}
15、将两个有序的顺序表合并为一个
cpp
// 将两个有序顺序表L1和L2合并为一个有序顺序表L3
void merge(SqList L1, SqList L2, SqList &L3) {
int i = 0; // L1的下标
int j = 0; // L2的下标
int k = 0; // L3的下标
// 合并两个有序表,直到其中一个表的元素全部被合并完
while (i < L1.length && j < L2.length) {
if (L1.data[i] <= L2.data[j]) {
L3.data[k++] = L1.data[i++]; // 将L1中的较小元素放入L3,并移动L1的下标
} else {
L3.data[k++] = L2.data[j++]; // 将L2中的较小元素放入L3,并移动L2的下标
}
}
//有剩下的说明都是最大的几个了
// 如果L1还有剩余元素,将它们全部复制到L3
while (i != L1.length) {
L3.data[k++] = L1.data[i++];
}
// 如果L2还有剩余元素,将它们全部复制到L3
while (j != L2.length) {
L3.data[k++] = L2.data[j++];
}
L3.length = k; // 更新L3的长度,即合并后有序表的长度
}
16、在有序的顺序表中查找特定元素,存在则与其后元素交换,不存在则插入该元素,顺序表始终有序。
cpp
// 折半算法
void find_x2(SqList &L, ElemType x) {
if (L.length == 0) {
cout << "kong" << endl; // 如果顺序表为空,则输出提示信息
}
bool found = false; // 标记是否找到元素
int low = 0; // 定义查找范围的下界
int high = L.length - 1; // 定义查找范围的上界
int mid; // 定义中间位置
// 使用折半查找法查找元素x
while (low <= high) {
mid = (low + high) / 2; // 计算中间位置
if (L.data[mid] == x) {
found = true;
break; // 如果找到元素,则跳出循环
} else if (x > L.data[mid]) {
low = mid + 1; // 调整查找范围的下界
} else {
high = mid - 1; // 调整查找范围的上界
}
}
int j; // 定义插入位置的下标
if (!found) { // 如果未找到该元素
for (j = L.length - 1; j >= 0; j--) { // 从后向前查找插入位置
if (L.data[j] > x) {
L.data[j + 1] = L.data[j]; // 将元素向后移动
}
}
L.data[j + 1] = x; // 插入新元素
L.length++; // 更新顺序表长度
}
}
代码中可能存在一些不足之处,仅供您参考。非常感谢您的宝贵意见和建议,以便我们能够不断改进和完善。