目录
一 、线性时间选择问题
二、众数问题
三、求逆序对数
四、棋盘覆盖问题
任务描述
给定线性无序数组n
个元素和一个正整数k
,1≤k≤n
,要求在线性时间找到这n个元素的第k
小。
相关知识
ϵn
和1−ϵn
(0<ϵ<1
)两部分,则能在线性时间完成找第k小任务;如果每次选择基准元素是最大值或最小值,则退化成最坏情况,时间复杂度为O(n2)
ϵn
和1−ϵn
(0<ϵ<1
)两部分.思想如下:n/5
组,将每组的中位数找到编程要求
完成一个冒泡排序函数:
void BubbleSort(Type a[],int p,int r)
完成根据基准元素x进行划分的函数:
int Partition(int a[],int p,int r,Type x)
完成线性时间选择数组a[p]~a[r]的第k小函数:
int Select(int a[],int p,int r,int k)
测试说明
函数中数组的元素是整型,返回值也是整型。
代码如下:
#include
using namespace std;
//p 为起始元素,r为最后一个元素地址,
//线性排序,找数组中第k小的数
int Select(int a[],int p,int r,int k);
//冒泡排序
void Bubblesort(int a[], int p, int r);
//交换两个元素位置
void Swap(int& a, int& b);
//将x作为基准数,将数组分割,返回x的位置
int Partition(int a[], int p, int r, int x);
int main()
{
int a[1000] = { 0 }, n, k;
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
cin >> k;
cout << Select(a, 0, n - 1, k) << endl;
return 0;
}
void Swap(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
int Partition(int a[], int p, int r, int x)
{
int i = p - 1, j = r + 1;
//将比x大的数换到x右边,小的数换到左边
while (true)
{
//i从x右边找到第一个大于等于x的数
while (a[++i] < x && i < r);
//j从最后一个元素开始找到第一个小于等于x得数
while (a[--j] > x && j > p);
if (i >= j)
break;//找到基准值的位置,j
Swap(a[i], a[j]);
}
return j;
}
void Bubblesort(int a[], int p, int r)
{
int n = r - p + 1;
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - 1 - i; j++)
{
if (a[j] > a[j + 1])
{
Swap(a[j], a[j + 1]);
}
}
}
return;
}
int Select(int a[], int p, int r, int k)
{
if (r - p < 75)//数组元素较少时,直接利用排序来找
{//随便用一种排序,这里用的是冒泡排序
Bubblesort(a, p, r);
return a[p + k - 1];//返回第k小的元素
}
//分成n/5组,每组5个元素,找到每组的中位数并放到数组前边
for (int i = 0; i <= (r - p - 4) / 5; i++)
{
int pp = p + i * 5, rr = p + 5 * i + 4;
Bubblesort(a, pp, rr);
int mid = (rr - pp + 1) / 2 + pp;
Swap(a[mid], a[p + i]);
}
//找各组中位数的中位数--在if语句中返回
int x = Select(a, p, p + (r - p - 4) / 5, (r - p - 4) / 10 + 1);//(r-p-4)/5元素的个数,(r-p-4)/10+1指要找第几个数。
//按照中位数划分,i为x在数组中的位置
int i = Partition(a, p, r, x);
//求比中位数x小的数组的长度
int len = i - p + 1;
if (k <= len)//则k在较小数中
{
return Select(a, p, i, k);
}
else
{
return Select(a, i + 1, r, k - len);
}
}
任务描述
给定含有n个元素的多重集合S,每个元素在S中出现的次数称为该元素的重数。多重集S中最大的元素称为众数。给定一个n个自然数组成的多重集合S,设计算法求其众数和重数。 例如:给出 S = [1,2,3,4,5,2,2] S其众数是2,重数是3
相关知识
编程要求
int GetMode(int a[],int p,int r,int &Count)
a[p]~a[r]
之间的众数,参数Count存放众数对应的重数。#include
using namespace std;
//排序,比基准值大的放右边,小于等于放左边,并计算基准值的重数,重数初始值为1
//p为数组第一个元素,r为数组最后一个元素,x为基准值的下标
void Partition(int a[], int p, int r,int x,int jizhun[])
{
int left = p , right = r;
while (left < right)
{
//从右边遍历找到第一个<=基准值的元素
while (a[right] > a[x] && right != left)//要注意,左右指针前进的顺序,因为以第一个元素为基准值,故右指针先走
{
if (a[right] == a[x])//判断等于为基准值的
jizhun[1]++;//基准值的重数
right--;
}
//从左边遍历找到第一个>基准值的元素
while (a[left] <= a[x] && left != right)
{
if (a[left] == a[x])
jizhun[1]++;
left++;
}
//交换位置,使左边的元素<=基准值,右边的元素>基准值
if (left != right)
{
int temp = a[left];
a[left] = a[right];
a[right] = temp;
}
}
//基准值归位
int temp = a[right];
a[right] = a[x];
a[x] = temp;
jizhun[0] = right;//存基准值下标
}
//函数返回值:众数
int getMode(int a[], int p, int r, int& Cnt)
{
int Cnt_m=1,Cnt_l = 1, Cnt_r = 1;//存左右边、基准值元素的重数
int mod_m=0,mod_l = 0, mod_r = 0;//存左右、基准值众数
int* jizhun = new int[2];//数组0存基准值下标,1存基准值重数
jizhun[0] = 0; jizhun[1] = 1;//设置初始值
//设置基准值为数组第一个元素
Partition(a,p,r,p,jizhun);//数组第一个元素为基准值,对数组进行划分
mod_m = a[p]; Cnt_m = jizhun[1];//基准值的重数和众数
int leftsize = jizhun[0] - jizhun[1] + 1;//基准值左边元素个数
int rightsize = r - jizhun[0];//基准值右边的元素个数
if (leftsize > jizhun[1])//左边元素个数多,递归求左边元素
{
mod_l=getMode(a, p, (jizhun[0] - jizhun[1]), Cnt_l);
}
if (rightsize > jizhun[1])//右边元素个数多,递归求右边元素
{
mod_r=getMode(a, (jizhun[0] + 1), r, Cnt_r);
}
//比较三者重数--特殊情况没有考虑---》即重数均为1或者有两个数的重数相等,再加一部分比较重数相等情况下的众数即可,这里可以单独写一个比较函数.
if (Cnt_l > Cnt_r)
{
if (Cnt_l > Cnt_m)
{
Cnt = Cnt_l;
return mod_l;
}
else
{
Cnt = Cnt_m;
return mod_m;
}
}
else
{
if (Cnt_r > Cnt_m)
{
Cnt = Cnt_r;
return mod_r;
}
else
{
Cnt = Cnt_m;
return mod_m;
}
}
}
int main()
{
int n, Cnt, mode;
cin >> n;
int* a = new int[n];
for (int i = 0; i < n; i++)
cin >> a[i];
mode = getMode(a, 0, n - 1, Cnt);
cout << mode<<"\n"<
对于不同的排序可以用逆序来评价它们之间的差异。 一个排列含有逆序的个数称为这个排列的逆序数。例如排列 263451 含有8个逆序,(2,1),(6,3),(6,4),(6,5),(6,1),(3,1),(4,1),(5,1)
,因此,该排列的逆序数就是8。 显然,由1,2,...,n
,n
个数组成的n!
个排列中,最小逆序数是0
,对应排列1,2,...,n
。 最大逆序数是2n(n−1)
,对应排列n,n−1,...,2,1
。 逆序数越大与原始排列差异越大。
第一行是一个整数n,表示该排列有n个数(n<=100000)。
第二行是n个不同的正整数,之间以空格隔开,表示该排列。
输出该排列的逆序数。
测试输入:
6
2
6
3
4
5
1
预期输出: 8
结合归并排序做
//归并排序及求逆序对
#include
using namespace std;
#define N 1000005
int a[N], b[N];//b为辅助数组
long long cnt;
void merge_sort(int l, int r)
{
// 如果整个区间中元素个数大于1,则继续分割
if (r - l > 0)
{
// 1、尽量划分为数量相等的2个子序列,并排序
int mid = (l + r) / 2;
merge_sort(l, mid);
merge_sort(mid + 1, r);
// 2、将2个有序的序列合并成一个有序序列,在左右有序的子数组合并的同时,统计逆序对数
/*********Begin***********/
int left = l, right = mid + 1;
int bj = l;//b数组的下标从l开始
while (left <= mid &&right <= r)//对数组进行排序,存到b数组中
{
if (a[right] < a[left])
{
b[bj] = a[right];
cnt=cnt+right-bj;//right-bj计算元素向前挪的位数,也就是逆序数
right++;
}
else
{
b[bj] = a[left];
left++;
}
bj++;
}
if (right <= r)//将剩余元素存入b中
b[bj++] = a[right++];
if (left <= mid)
b[bj++] = a[left++];
/*********End***********/
// 3、将辅助数组b中排好序的元素复制到a中
for (int i = l; i <= r; i++)
a[i] = b[i];
}
}
int main()
{
int n;
while (cin >> n)
{
for (int i = 1; i <= n; i++)
cin >> a[i];
cnt = 0;
merge_sort(1, n);
cout << cnt << endl;
}
return 0;
}
在一个2k×2k
个方格组成的棋盘中,恰有一个方格与其它方格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。
例如:4×4
的棋盘的一种覆盖方法为:
编写函数,输出给定k
的一种棋盘覆盖。
根据提示,在右侧编辑器补充代码。
输入为三个正整数: size
dr
dc
其中(size=2k,0≤k≤5),(dr,dc)
是特殊方格的位置,且0≤dr,dc≤size−1
输出棋盘覆盖方案,特殊方格处输出-1,其他位置处同一编号的L形骨牌用同一个数字表示,数字占宽4格,右对齐。
测试输入:8
3
2
预期输出:
2 2 3 3 7 7 8 8
2 1 1 3 7 6 6 8
4 1 5 5 9 9 6 10
4 4 -1 5 0 9 10 10
12 12 13 0 0 17 18 18
12 11 13 13 17 17 16 18
14 11 11 15 19 16 16 20
14 14 15 15 19 19 20 20
#include
#include
# define SIZE 32
using namespace std;
int tile = 0;
int Board[SIZE][SIZE];
//棋盘以(tr,tc)为左上角,以size为边长,特殊方格位置在(dr,dc),ChessBoard函数用分治法
//完成它四个子棋盘覆盖
void ChessBoard(int tr, int tc, int dr, int dc, int size)
{
/************Begin**************/
//递归出口
if (size == 1)
return;
int s = size / 2;//分割棋盘
int t = tile++;//记录本层骨牌序号
//判断特殊方格在不在左上棋盘上
if (dr < tr + s && dc < tc + s)
{
ChessBoard(tr, tc, dr, dc, s);//特殊方格在左上棋盘上
}
else
{
Board[tr + s - 1][tc + s - 1] = t;//用t号L型覆盖右下角
ChessBoard(tr, tc, tr + s - 1, tc + s - 1, s);//递归覆盖其余方格
}
//判断特殊方格在不在右上棋盘上
if (dr < tr + s && dc >= tc + s)
{
ChessBoard(tr, tc + s, dr, dc, s);
}
else
{
Board[tr + s-1][tc + s ] = t;
ChessBoard(tr, tc + s, tr + s - 1, tc + s, s);
}
//判断特殊方格在不在左下棋盘
if (dr >= tr + s && dc < tc + s)
{
ChessBoard(tr + s, tc, dr, dc, s);
}
else
{
Board[tr + s][tc + s - 1] = t;
ChessBoard(tr + s, tc, tr + s, tc + s - 1, s);
}
//判断特殊方格在不在右下棋盘
if (dr >= tr + s && dc >= tc + s)
{
ChessBoard(tr + s, tc + s, dr, dc, s);
}
else
{
Board[tr + s][tc + s] = t;
ChessBoard(tr + s, tc + s, tr + s, tc + s, s);
}
/************End**************/
}
int main()
{
/************Begin**************/
int dr = 0, dc = 0;//存特殊方格位置
int size = 0;//存棋盘大小
cin >> size >> dr >> dc;
ChessBoard(0, 0, dr, dc, size);
Board[dr][dc] = -1;//设置特殊方格为-1
/************End**************/
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
cout << setw(4) << Board[i][j];
cout << endl;
}
return 0;
}