//20页
//搜索整个顺序表,查找最小值元素并记住其位置,用结束后用最后一个元素补空
//z最小值得位置
bool Del_Min(Sqlist &L,ElemType &value) {
if(L.length==0)
return flase;
//设置最小值
value=L.data[0];
for(int i=1;i<=length;i++)
{
if (L.data[i])<value)
{
value=L.data[i];
pos=i;
}
}
L.data[pos]=L.data[L.length-1]; //用最后一个元素实现覆盖,及删除
L.length--;
return ture;
}
}
暴力就是再整一个数组,原数组从末尾循环遍历放到新数组里
要求空间复杂度O(1)的话,从头循环到中间,交换头尾元素
偶数或者奇数个元素,循环到中间,都是 < length/2
,因为如果长度为4或者5,都是循环到下标为1即停,为5的时候最中间的元素(下标为2)不需要移动。
时间复杂度O(n)
void reverse(Sqlist &list){
//定义一个辅助变量
Element temp;
for(int i=0;i<length/2;i++)
{ //交换两个元素的位置
temp=L.data[i];
L.data[i]=L.data[length-i-1];
L.data[length-i-1]=temp;
}
}
void del_x(SqList &list ,int x){
//空间复杂度o(1)是指要在一个循环内搞定
int k=0;
for (int i=0;i<list.length;i++) {
if (list.data[i]!=x){
list.data[k]=list.data[i];
k++;
}
}
//新建了一个数组去接收不等于x的值
//后面的值直接不要了;
list.length=k;
}
//方法二 类似与快速排序
一次循环 -> 双指针
类似快排,从两端向中间移动,将左边的x与右边的非x交换
void del_x2(SqList &list ,int x){
int i=-1,j=list.length,k=0;
while(i<j){
//list.data 原来数组
while(list.data[++i]!=x);
while(list.data[--j]==x) k++;
if (i<j){
swap(list.data[i],list.data[j]);
k++;
}
list.length=k;
}
//有序表删除的必然是s,t之间的所有元素
void del_st(Sqlist &list,int s,int t){
if (s>=t||)length==0){
return false;
}
int k=0;
for(int i=0;i<length;i++ ){
if(list.data[i]<s||list.data[i]>t){
list.data[k++]=list.data[i];
}
}
//直接不用管后面的值
list.length=k;
}
//王道书上答案狗都不看
本题与上题的区别,因为是有序表,所有删除的元素必须是相连的整体。从前向后扫描顺序表L,用k记录下元素值在s到t之间的元素的个数。若其值不在s到t之间,则前移k个位置;否则执行k++.
*/
bool Del_s_t(SqList &L,int s,int t)
{
int i,k=0;
if(L.length==0||s>=t)
return false;
for(i=0;i<L.length;i++)
{
if(L.data[i]>=s && L.data[i]<=t)
k++;
else
L.data[i-k] = L.data[i];//当前元素前移k个位置
}
L.length -= k; //长度减小
return true;
}
void del_same(SqList &list) {
if (list.length == 0) return;
// 1.新开一个数组
SqList copied = list; //把整个数组都复制过来了
copied.data[0] = list.data[0]; //是不是多余,把数据部分也复制过来
int k=0;
for(int i=1;i<list.length;i++)
{
if(list.data[k]!=list.data[i])
{
copied.data[++k]=list.data[i];
}
copied.length=k+1; //确定长度之后后面部分就不要了
list=copied;
}
}
仔细想想,其实并不需要两个数组,双指针就可以
前一个存储,后一个判断
void del_same2(SqList &list) {
if (list.length == 0) return;
//利用双指针进行判断
int k=0;
for (int i=1;i<list.length;i++)
{
//i就是靠后的那个指针
//把不相等的直接,放在下一个位置
if(list[k]!=list.data[i]){
list,data[++k]=list.data[i];
}
}
list.length = k + 1;
}
//两个表合并问题
Sqlist merge(Sqlist A,Sqlist B){
Sqlist C;
if(A.length+B.length >MaxSize){
return flase;
}
int i=0; j=0; k=0;
while (i<A.length&&j<B.length){
if (A.data[i]<B.data[j])
{
C.data[k++]=A.data[i++];
}
else
{
C.data[k++]=A.data[j++];
}
}
//2.剩下的结果全部加入表中,两个循环只会有一个运行
//i,j 记录的是键盘的较短那个元素结束的值
while(i<A.length)
C.data[k++]=A.data[i++];
while (j < B.length)
C.data[k++] = B.data[j++];
// 3.返回结果表
C.length = k;
return C;
}
补充一下归并排序的代码
void Merge(int A[], int left, int mid, int right) //合并操作的代码实现
{
int *B = new int[right - left + 1]; //申请一个辅助数组B[],与传递过来的序列数组等长
//图中的三个辅助标记(工作指针)
int i = left; //指向待排序子序列数组A[left:mid]中当前待比较的元素
int j = mid + 1; //指向待排序子序列数组A[mid+1:right]中当前待比较的元素
int k = 0; //k指向辅助数组B[]中待放置元素的位置
while (i <= mid && j <= right) //当i和j都指向未超过数组范围的时候
{ //从小到大排序,将A[i]和A[j]中的较小元素放入B[]中
if (A[i] <= A[j]) //当前半部分数组A[left:mid]的值不大于后半部分数组A[mid+1:right]的值时,将前半部分数组辅助标记对应的值存入辅助数组中(具有稳定性)
B[k++] = A[i++]; //存入辅助数组B[]中,且与之对应的辅助标记后移
else //否则将后半部分辅助标记对应的值存入B[]中
B[k++] = A[j++]; //存入辅助数组且与之对应的辅助标记后移
}
while (i <= mid) //对序列A[left:mid]剩余的部分依次进行处理,与图中的(5)对应
B[k++] = A[i++]; //将辅助标记对应的值存入辅助数组中且辅助标记后移
while (j <= right) //对序列A[mid+1:right]剩余的部分依次进行处理
B[k++] = A[j++]; //将辅助标记对应的值存入辅助数组中且辅助标记后移
for (i = left, k = 0; i <= right; i++) //将合并后的序列复制到原来的A[]序列
A[i] = B[k++];
delete[] B;
递归调用这个过程
//递归形式的归并排序算法
void MergeSort(int A[], int left, int right) //归并排序
{
if (left < right) //当数组内的元素数大于1时进行二分操作,只有一个元素的时候,不作任何处理直接结束
{
int mid;
mid = (left + right) / 2; //计算中间位置
MergeSort(A, left, mid); //对数组A[left:mid]中的元素进行归并排序
MergeSort(A, mid + 1, right); //对数组A[mid+1:right]中的元素进行归并排序
Merge(A, left, mid, right); //进行合并操作
}
}
已知在一位数组A[m+n]中依次存放两个线性表(a1,a2,a3,…,am)和(b1,b2,b3,…bn)。编写一个函数,将数组中两个顺序表的位置互换,即将(b1,b2,b3,…bn)放在(a1,a2,a3,…,am)的前面。
算法思想:先将数组A[m+n]中的全部元素(a1,a2,a3,…,am,b1,b2,b3,…,bn)原地逆置为(bn,bn-1,…b1,am,am-1,…,a1),再对前n个元素和后m个元素分别使用逆置算法,即可得到(b1,b2,…,bn,a1,a2,…,am),从而实现顺序表的位置互换。
void Reverse(int a[],int left,int right,int arraySize) {
//先进行全部逆置
if (left >= right || right >= arraySize)
return false;
int mid=(left+right)/2;
for (int i=0;i<mid-left;i++){ //不管奇数偶数都可以这样
int temp=A[left+i];
A[left+i]= A[right-i];
A[right-i]=temp;
}
}
void Exchange(int A[],int m,int n,int arraySize){
Reverse (A,0,m+n-1,arraySize);
Reverse (A,0,n-1,arraySize);
Reverse (A,n,m+n-1,arraySize);
}
算法思想:折半查找法。
//折半查找法
typedef int ElemType;
void SearchExchangeInsert(ElemType A[],int n, ElemType x){
int low =0,high=n-1,mid;
int i;
//x是要查找的那个数
while (low<=high){
mid =(low+high)/2;
if (A[mid]==x)
break;
else if (A[mid]<x)
low =mid+1;
else
high=mid-1;
}
//下面的if语句只会执行一个
if (A[mid]==x && mid !=n-1) //说明进入了第一个循环并且找到了
{
int t =A[mid];
A[mid]=A[mid+1];
A[mid+1]=t;
//x就是mid 把x放到后面一个位置
}
//循环结束找不到x的情况
if(low >high){
//把x插入在增序的状态
for(i=n-1;i>high;i--) //high 与low互换位置
A[i+1]=A[i];//后移元素
A[i+1]=x; //插入x
}
}
要实现R中序列循环左移P个位置,只需先将R中前P个元素逆置,再将剩下的元素逆置,最后将R中所有的元素再整体做一次逆置操作即可。
//要实现R中序列循环左移P个位置,只需先将R中前P个元素逆置,再将剩下的元素逆置,最后将R中所有的元素再整体做一次逆置操作即可。
void Reverse(int R[],int left ,int right,int arraySize) {
int t;
int i;
//算法的健壮性
if (left >=right ||right >=arrraySize ){
return;
}
for(i = 0; i <= (left + right) / 2; i++) {
t=R[left+i];
R[left+i]=R[right-i];
R[right-i]=t;
}
} //可以实现逆置的函数
void Exchange(nt R[],int left ,int right,int arraySize) {
Reverse(R,0,p-1,arraySize);
Reverse(R,p,n-1,arraySize);
Reverse(R,0,n-1,arraySize);
}
上述算法中三个Reverse函数的空间复杂度分别为O(p/2),O((n-p)/2)和O(n/2),故设计的算法的时间复杂度为O(n),空间复杂度为O(1).
分别求两个升序序列A,B的中位数,设为a和b,求序列A,B的中位数过程如下:
若a=b,则a或b即为所求中位数,算法结束。
若a 若a>b,则舍弃序列A中较大的一半,同时舍弃序列B中较小的一半,要求两次舍弃的长度相等。
在保留的两个升序序列中,重复上述123过程,直到两个序列中只含有一个元素时为止,较小者即为所求的中位数
//上述算法的时间复杂度为O(log2n),采用空间复杂度为O(n)
int M_Search(int A[],int B[],int n){
//定义变量
int m1,m2,s1,s2,d1,d2;
s1=s2=0;
d1=d2=n-1;
while(s1!=d1 ||s2!=d2) //当两个数组不为空时
{
//求第一个数组的中位数
m1=(s1+d1)/2;
m2=(s2+d2)/2;
if (A[m1]==B[m2]){
return A[m1];
}
if(A[m1]<B[m2])//A的中位数比B的中位数小
{
//判断一下当前元素是否为奇数
if((s1+d1)%2==0)//当前比较元素为
{
s1=m1;//A的比较范围向后移动,保留中间点
d2=m2
}
else{//偶数的情况
s1=m1+1;//A的比较范围向后移动,不保留中间点
d2=m2;
}
}
else{ //满足条件三
if ((s2 + d2) % 2 == 0) {//若元素个数为奇数
d1 = m1; //舍弃A中间点以后的部分并保留中间点
s2 = m2; //舍弃B中间点以前的部分并保留中间点
}
else { //元素个数为偶数
d1 = m1; //舍弃A中间点以后部分并保留中间点
s2 = m2 + 1;//舍弃B中间点及中间点以前的部分
}
}
}
return A[s1] < B[s2] ? A[s1] : B[s2];
}
//暴力解法构建一个数组
#include "ds.h"
/**
* 暴力解:空间换时间,开一个新数组,记录每一个数字的出现次数然后判断
*/
int find_main_bf(int A[], int len) {
int *tmp = (int *) malloc(sizeof(int) * len);
memset(tmp, 0, sizeof(int) * len);
for (int i = 0; i < len; i++) {
tmp[A[i]]++;
if (tmp[A[i]] > len / 2) {
return A[i];
}
}
return -1;
}
时间复杂度o(n),空间复杂度O(n)
//与上题的暴力解法相似 以空间换时间
int findMissMin(int A[],int n)
{
int i,*B; //利用这个指针数组来存出现的数
//构建的数组长度为n
B=(int *)malloc(sizeof(int)*n);
memset(B,0,sizeof(int)*n); //给B指针中赋初值为0
for(i=0;i<n;i++){
if(A[i]>0&&A[i]<=n) //数组里面的值是从1-n记录的
B[A[i]-1] =1; //只要记录出现过就可
}
for(i=0;i<n;i++)
{
if(B[i]==0) break;
}
return i+1;
}
//时间复杂度,A遍历一次,B遍历一次,两次循环内操作步骤为O(1)量级,因此时间复杂度为O(n)。空间复杂度:额外分配了B[n],空间复杂度为O(n)。