数据结构代码合集

一、排序算法

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++; // 更新顺序表长度
    }
}

代码中可能存在一些不足之处,仅供您参考。非常感谢您的宝贵意见和建议,以便我们能够不断改进和完善。

相关推荐
tekin14 分钟前
Go、Java、Python、C/C++、PHP、Rust 语言全方位对比分析
java·c++·golang·编程语言对比·python 语言·php 语言·编程适用场景
liruiqiang0517 分钟前
机器学习 - 投票感知器
人工智能·算法·机器学习
小禾苗_1 小时前
C++ ——继承
开发语言·c++
OrangeJiuce2 小时前
【QT中的一些高级数据结构,持续更新中...】
数据结构·c++·qt
程序员-King.5 小时前
【接口封装】——13、登录窗口的标题栏内容设置
c++·qt
学编程的小程5 小时前
LeetCode216
算法·深度优先
leeyayai_xixihah5 小时前
2.21力扣-回溯组合
算法·leetcode·职场和发展
01_5 小时前
力扣hot100——相交,回文链表
算法·leetcode·链表·双指针
萌の鱼5 小时前
leetcode 2826. 将三个组排序
数据结构·c++·算法·leetcode
Buling_05 小时前
算法-哈希表篇08-四数之和
数据结构·算法·散列表