数据结构专栏:
本章目录
删除顺序表中最小值
/*
从顺序表中删除具有最小值的元素(假设唯一),并由函数返回被删除的值,
空出的位置有最后一个元素代替,若顺序表为空则显示错误信息并退出运行
*/
/*
分析:我们可以将顺序表看做数组,即我们要找出数组中最小的元素位置,
并删除它,而这里的删除之后我们会用最后一个元素替代它。综上所诉,我们
可以设置一个位置标签,遍历一次找到最小的那个元素的位置,然后直接将最后一个元素的值赋予该位置
*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void deleteMin(int *arr,int len) {
if (!len)exit(0);//数组为空,退出运行
int minPos,min=*arr;
for (int i = 1; i < len;i++) {
if (min>*(arr+i)) {
min = *(arr + i);
minPos = i;
}
}
//找到最小值之后,进行交换
*(arr + minPos) = *(arr + len - 1);
*(arr + len - 1) = NULL;
}
int main() {
int n;//手动输入数组长度
printf("请输入数组长度:");
scanf("%d",&n);
int *arr = (int *)malloc(sizeof(n));//动态分配数组
printf("请输入数组的元素值,输入一个以enter结束");
for (int i = 0; i < n;i++)
scanf("%d",arr+i);
for (int i = 0; i < n;i++) //原始数组
printf("%d",*(arr+i));
printf("\n");
deleteMin(arr,n);
for (int i = 0; i < n-1; i++)//当前数组
printf("%d", *(arr + i));
return 0;
}
逆置数据元素
/*
实现一个空间复杂度为O(1)的顺序表逆置算法。
分析:从第一个元素开始遍历到中间元素,使用中间变量对a[i]与a[n-i+1]进行调换,即可完成
*/
#include <stdio.h>
void revert(int *arr,int len) {
int tmp;
for (int i = 0; i < len/2;i++) {
tmp = *(arr + i);
*(arr + i) = *(arr + len - i - 1);
*(arr + len - i - 1) = tmp;
}
}
int main() {
int arr[] = {1,2,3,4,5,6,7};//这里的数组也可以手动赋值
int len = sizeof(arr)/4;
revert(arr,len);
for (int i = 0; i < len;i++) printf("%d ",*(arr+i));
return 0;
}
删除数组中的指定值
/*
长度为n,删除值为x的数据元素,要求时间复杂度为O(n),空间复杂度O(1)。
分析:
用k记录删除元素个个数,一边遍历顺序表一边统计k,并将不等于x的元素向前挪k个位置
*/
#include <stdio.h>
void deleteX(int *arr,int n,int len) {
int k = 0,i=0;//进行计数
while (i<len) {
if (*(arr+i)==n) {
k++;
}
else {
*(arr +i- k) = *(arr + i);
}
i++;
}
for (int i = 0; i < len-k; i++) {
printf("%d ", *(arr + i));
}
}
int main() {
int arr[] = {1,2,3,2,4,2};//也可以手动赋值的
int len = sizeof(arr) / sizeof(int);
deleteX(arr,2,len);//要删除的元素也可以有用户输入
return 0;
}
删除指定范围值
/*
从顺序表中删除给定区间s~t之间的值,若给定区间不合理或顺序表为空,则显示错误信息并退出
*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void deletePoint(int *arr, int s, int t, int len) {
int k = 0, i = 0;
for (i = 0; i < len && *(arr + i) < s; i++);//找到第一个大于等于s的元素
if (i >= len) return;
for (k = i; k <= len && *(arr + k) <= t; k++);//找到第一个大于t的元素
for (; k < len; i++, k++) *(arr + i) = *(arr + k);
printf("当前数组为:");
for (int j = 0; j < i; j++) printf("%d ", *(arr + j));
}
int main() {
int arr[] = { 1,2,4,6,10,15 };//也可以由用户输入
int s, t;//用于给定区间范围
int len = sizeof(arr) / sizeof(int);
printf("请输入区间范围,s<t\n");
do {
printf("s=");
scanf("%d", &s);
printf("t=");
scanf("%d", &t);
} while (s > t);
deletePoint(arr, s, t, len);
return 0;
}
删除重复值
/*
删除有序顺序表中重复的值,使表中所有的元素不同。
分析:
因为是有序顺序表,我们可以直接进行遍历,将不同的元素存回数组
*/
#include <stdio.h>
int deleteRepitition(int *arr,int len) {//由于C语言无法直接修改数组长度,我们返回去重后的数组长度
int k = 0;//用来记录下标
for (int i = 0; i < len;i++) {
if (*(arr+i)-*(arr+i+1)) {
*(arr + k++) = *(arr + i);
}
}
if (*(arr+len-1) == *(arr+len)) {//如果最后一个元素恰好和后一个存储空间元素等,则需要单独处理。
*(arr + k++) = *(arr+len-1);
}
return k;
}
int main() {
int arr[] = {1,1,2,2,2,3,3,4,5};
int len = sizeof(arr) / sizeof(int);
len=deleteRepitition(arr,len);
for (int i = 0; i < len; i++) printf("%d ",*(arr+i));
return 0;
}
合并两个顺序表
/*
将两个有序顺序表合并成一个新的顺序表,并由函数返回新的结果顺序表
分析:
手动开辟一个数组空间,其大小要大于等于原始两个数组的长度,之后进行遍历,依次取较小值赋给新数组
*/
#include <stdio.h>
#include <stdlib.h>
int *merge(int* l1,int* l2,int len1,int len2) {
int* newArr = (int*)malloc(len1+len2);
//if (*newArr == NULL) return;
int k = 0,i = 0,j = 0;
for (; i < len1&&j < len2;) {
if (*(l1+i)<*(l2+j)) {
*(newArr + k++) = *(l1 + i++);
}
else {
*(newArr + k++) = *(l2 + j++);
}
}
while (i < len1) {
*(newArr + k++) = *(l1 + i++);
}
while (j < len2) {
*(newArr + k++) = *(l2 + j++);
}
return newArr;
}
int main() {
int l1[] = {1,3,5,8};
int l2[] = {2,4,4,6,7};
int len1 = sizeof(l1) / sizeof(int);
int len2 = sizeof(l2) / sizeof(int);
int *res;
res = merge(l1,l2,len1,len2);
for (int i = 0; i < len1 + len2; i++) printf("%d ",*(res+i));
return 0;
}
折半查找指定元素
/*
线性表递增有序存放,设计一算法,用最少的时间查找值为x的元素,
若找到则将其与后续元素位置相交换,否则,插入使其仍然有序
分析:
题目告知这是一个递增有序表,要使时间最少,我们可以采取折半查找的方式,这样比顺序查找省时
*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int findAndExchange(int* arr, int n, int len) {//返回数组长度
int low = 0, high = len, middle;
while (low <= high && (middle = (low + high) / 2)) {
if (n == arr[middle])break;
if (n < arr[middle]) {
high = middle - 1;
}
else {
low = middle + 1;
}
}
if (low > high) {//未找到,插入
int i;
for (i = len - 1; i > high; i--) {//元素所在位置及其之后的元素后移
*(arr + i + 1) = *(arr + i);
}
*(arr + i + 1) = n;
return len + 1;
}
else {//找到,对调
int tmp = *(arr + middle);
*(arr + middle) = *(arr + middle + 1);
*(arr + middle + 1) = tmp;
return len;
}
}
int main() {
int arr[10] = { 1,3,5,6,9,12,45,78 };
int n, len;
printf("请输入你要查找的元素,n=");
scanf("%d", &n);
len = findAndExchange(arr, n, 8);
for (int i = 0; i < len; i++) printf("%d ", *(arr + i));
return 0;
}
寻找中位数-2011
/*
一个升序序列的中间位置的元素称为中位数,两个序列的中位数则是这两个序列升序排好序后的中间的数为中位数,现有两个等长的生序序列
A、B,找出它们的中位数。
分析:
我们可以先将这两个序列合并成一个升序序列,然后即可求出中位数,但是这样时间复杂度为O(n),空间复杂度也为O(n);
我们也可以采用归并排序的想法,先求出两个序列的总元素的个数,因为他们等长,只需求出一个序列的长度,中间位置即为len,
之后我们开始依次比较,并进行计数count,当count=len时,该元素即为中位数,这样时间复杂度为O(n),空间复杂度为O(1)
*/
#include <stdio.h>
int findMidNum1(int *l1, int *l2, int len) {
int count = 0, middle;
for (int i = 0, j = 0; *(l1 + i) && *(l2 + j);) {//依次比较,较小者出列
if (count++ == len) {//判断是否到达中位数的位置
middle = *(l1 + i) < *(l2 + j) ? *(l1 + i) : *(l2 + j);//取出中位数
break;
}
else {
*(l1 + i) < *(l2 + j) ? i++ : j++;//没有到,继续归并
}
}
return middle;
}
int findMidNum2(int *l1, int *l2, int len) {
int s1 = 0, d1 = len - 1, s2 = 0, d2 = len - 1, m1, m2;//两个表的开始结束位置及中间位置
while (s1 != d1 || s2 != d2) {
m1 = (s1 + d1) / 2;
m2 = (s2 + d2) / 2;
if (l1[m1] == l2[m2]) {//此时位置就是中位数的位置
return l1[m1];
}
if (l1[m1] < l2[m2]) {//此时舍弃m1左边元素,m2右边元素,但考虑到有奇偶情况,分开讨论
if ((s1 + d1) % 2 == 0) {//如果此时操作的序列为奇数,则要保留中间点
s1 = m1;//舍弃m1的左边部分
d2 = m2;//舍弃m2的右边部分
}
else {//而如果操作序列为偶数,则舍弃m1的左边及其中心点,舍弃m2的右边
s1 = m1 + 1;
d2 = m2;
}
}
else {//此时舍弃m2左边元素,m1右边元素,也需要分奇偶讨论
if ((s2 + d2) % 2 == 0) {//如果此时操作的序列为奇数,则要保留中间点
d1 = m1;//舍弃l1的左边部分
s2 = m2;//舍弃l2的右边部分
}
else {//而如果操作序列为偶数,则舍弃m1的左边及其中心点,舍弃m2的右边
d1 = m1;
s2 = m2 + 1;
}
}
}
return l1[s1] < l2[s2] ? l1[s1] : l2[s2];
}
int main() {
int l1[] = { 1,3,4,7,9 ,10,11};// 1 2 3 4 4 6 7 8 9 10 10 11
int l2[] = { 2,4,6,8,10 };
int len = sizeof(l1) / sizeof(int);
int middle;
middle = findMidNum2(l1, l2, len - 1);
printf("中位数是:%d ", middle);
return 0;
}
求解三元组最短路径-2020
/*
已知三个升序整数数组a[l], b[m]和c[n]。请在三个数组中各找一个元素,是的组成的三元组距离最小。三元组的距离定义是:假设a[i]、b[j]和c[k]是一个三元组,那么距离为:
Distance = max(|a[ I ] – b[ j ]|, |a[ I ] – c[ k ]|, |b[ j ] – c[ k ]|)
请设计一个求最小三元组距离的最优算法,并分析时间复杂度。
*/
#include <stdio.h>
#include <math.h>
int dis(int a,int b,int c) {//返回距离
return abs(a - b) + abs(b - c) + abs(a - c);
}
int min(int a,int b,int c) {//返回当前最小值,此时便移动它的指针
if (a < b&&a < c) return a;
else if(b < c&&b < a) return b;
else return c;
}
int findMinofTrip(int *s1,int *s2,int *s3,int len1,int len2,int len3) {
int i=0, j=0, k = 0;//数组下标,遍历数组
int Max = 1000;//设定一个最大值,假定为1000
int nowD = 0;
while (i<len1&&j<len2&&k<len3) {
nowD = dis(s1[i],s2[j],s3[k]);
if (nowD < Max) Max = nowD;//进行更新
if (s1[i] == min(s1[i], s2[j], s3[k])) i++;
else if (s2[j] == min(s1[i], s2[j], s3[k])) j++;
else k++;
}
return Max;
}
int main() {
int S1[] = { -1,0,9 };
int S2[] = { -25,-10,10,11 };
int S3[] = {2,9,17,30,41};
int minD = findMinofTrip(S1,S2,S3,3,4,5);
return 0;
}