当前位置: 首页 > news >正文

信息学奥赛一本通 1239:统计数字(禁STL及相关调用)

【题目链接】

ybt 1239:统计数字
注意:本题目禁止使用STL及包含可以使用的相关调用
一本通中限制不许使用STL,那么引入头文件不能写<bits/stdc++.h>,只能写<iostream>,否则不允许提交。

【题目考点】

1. 二分查找
2. 计数排序
3. 快速排序
4. 归并排序
5. 二叉搜索树

【解题思路】

解法1:O(nlogn)排序+遍历计数

首先手写快速排序或归并排序代码,而后遍历整个数组,统计每种数字出现的个数并输出。
遍历时,设当前在看的数值为num,数值为num的元素个数为ct。当遍历到一个与num不同的元素时,输出已经统计好的num与其个数ct。而后将num设为新的元素,ct设为1。
在遍历完整个数组后,再将最后一段的数值num和数值个数ct输出。
该解法时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

解法2:二分查找+插入排序

设一个数组a,每个元素是一个结构体对象,保存数值num与其个数ct。a数组中的元素关于属性num是升序的,可以进行二分查找。
对于每个输入的数值v,在a中二分查找num为v的元素,

  • 如果存在num为v的元素,该数值num的个数应该增加1,即该元素的ct属性增加1。
  • 如果不存在num为v的元素,则在a的末尾添加num为v、个数ct为1的元素,而后将新加入的元素插入到前面的有序序列中,该过程是一趟插入排序。

最后遍历a数组,输出每个元素的数值num和个数ct。
当然二分查找可以选择“找大于等于v的最小值”,或“找小于等于v的最大值”的写法。
该解法时间复杂度为 O ( n l o g m + m 2 ) O(nlogm+m^2) O(nlogm+m2),其中m为数字种类数,m最大为10000。

解法3:二叉搜索树

二叉搜索树每个结点保存数值num和其个数ct。该二叉搜索树中左孩子的num小于根的num小于右孩子的num。
每输入一个数值v,对二叉查找树进行插入操作

  • 如果不存在num为v的结点,则新增结点,其num为v,ct为1。
  • 如果存在num为v的结点,则该结点的ct增加1。

中序遍历二叉搜索树,输出每个结点的num和ct。以该顺序输出,自然是以num升序顺序输出。
m为数字种类数,m最大为10000,二叉搜索树中最多有m个结点。
由于一般二叉搜索树无法保证高度为 l o g m logm logm,该解法每次插入复杂度为 O ( m ) O(m) O(m),总体最坏时间复杂度为 O ( n ∗ m ) O(n*m) O(nm)
如果能写出红黑树或AVL或Treap,可以将时间复杂度降到 O ( n l o g m ) O(nlogm) O(nlogm)

【题解代码】

解法1:O(nlogn)排序+遍历计数

  • 写法1:手写快速排序
#include<iostream>
using namespace std;
#define N 200005
int n, a[N], ct, num;
void quickSort(int l, int r)
{
	if(l >= r)
		return;
	int pivot = a[rand()%(r-l+1)+l], i = l, j = r;
	while(i <= j)
	{
		while(a[i] < pivot)
			i++;
		while(a[j] > pivot)
			j--;
		if(i <= j)
		{
			swap(a[i], a[j]);
			i++, j--;
		}
	}
	quickSort(l, j);
	quickSort(i, r);
}
int main()
{
	srand(time(NULL));
	cin >> n;
	for(int i = 1; i <= n; ++i)
		cin >> a[i];
	quickSort(1, n);
	num = a[1];//当前在看的数字 
	for(int i = 1; i <= n; ++i)
	{
		if(a[i] == num)
			ct++;
		else
		{
			cout << num << ' ' << ct << endl;
			ct = 1;
			num = a[i];
		}
	} 
	cout << num  << ' ' << ct << endl;
    return 0;
}
  • 写法2:手写归并排序
#include<iostream>
using namespace std;
#define N 200005
int n, a[N], ct, num, t[N];
void mergeSort(int l, int r)
{
	if(l >= r)
		return;
	int mid = (l+r)/2;
	mergeSort(l, mid);
	mergeSort(mid+1, r);
	int i = l, j = mid+1, k = l;
	while(i <= mid && j <= r)
	{
		if(a[i] <= a[j])
			t[k++] = a[i++];
		else
			t[k++] = a[j++];
	}
	while(i <= mid)
		t[k++] = a[i++];
	while(j <= r)
		t[k++] = a[j++];
	for(int i = l; i <= r; ++i)
		a[i] = t[i];
}
int main()
{
	srand(time(NULL));
	cin >> n;
	for(int i = 1; i <= n; ++i)
		cin >> a[i];
	mergeSort(1, n);
	num = a[1];//当前在看的数字 
	for(int i = 1; i <= n; ++i)
	{
		if(a[i] == num)
			ct++;
		else
		{
			cout << num << ' ' << ct << endl;
			ct = 1;
			num = a[i];
		}
	} 
	cout << num  << ' ' << ct << endl;
    return 0;
}

解法2:二分查找+插入排序

  • 写法1:二分查找数值是否存在
#include <iostream>
using namespace std;
#define N 10005
struct Num
{
	int num, ct;//num:数字 ct:个数 
};
Num a[N];
int an;//ai:a中元素的个数
int main()
{
    int n, v;
    scanf("%d", &n);
    scanf("%d", &v);//先输入第1个值 
    a[++an] = Num{v, 1};//num为v, ct为1
    for(int i = 2; i <= n; ++i)
    {
        scanf("%d", &v);
        int l = 1, r = an, m;//二分查找a[1]~a[an]中是否有v
        while(l <= r)
        {
            m = (l+r)/2;
            if(v < a[m].num)
                r = m-1;
            else if(v > a[m].num)
                l = m+1;
            else
            	break;
        }
        if(a[m].num == v)//找到数值v 
        	a[m].ct++;//v的个数增加1 
        else
        {//把x插入a
            a[++an] = Num{v, 1};
			for(int j = an; j > 1; --j)//将a[an]插入到a[1]~a[an-1]这一有序序列中 
            {
            	if(a[j].num < a[j-1].num)
            		swap(a[j], a[j-1]);
                else
                	break;
        	}
        }
    }
    for(int i = 1; i <= an; ++i)
        printf("%d %d\n", a[i].num, a[i].ct);
    return 0;
}
  • 写法2:二分查找满足条件的最小值
#include <iostream>
using namespace std;
#define N 10005
struct Num
{
	int num, ct;//num:数字 ct:个数 
};
Num a[N];
int an;//ai:a中元素的个数
int main()
{
    int n, v;
    scanf("%d", &n);
    scanf("%d", &v);//先输入第1个值 
    a[++an] = Num{v, 1};//num为v, ct为1
    for(int i = 2; i <= n; ++i)
    {
        scanf("%d", &v);
        int l = 1, r = an, m;//二分查找大于等于v的最小值 
        while(l <= r)
        {
            m = (l+r)/2;
            if(a[m].num >= v)
            	r = m-1;
            else
            	l = m+1;
        }
        if(a[l].num == v)//大于等于v的最小值就是数值v,即数值v存在 
        	a[l].ct++;//v的个数增加1 
        else
        {//把x插入a
            a[++an] = Num{v, 1};
			for(int j = an; j > 1; --j)//将a[an]插入到a[1]~a[an-1]这一有序序列中 
            {
            	if(a[j].num < a[j-1].num)
            		swap(a[j], a[j-1]);
                else
                	break;
        	}
        }
    }
    for(int i = 1; i <= an; ++i)
        printf("%d %d\n", a[i].num, a[i].ct);
    return 0;
}
解法3:二叉搜索树
#include <iostream>
using namespace std;
#define N 10005
struct Node
{
	int num, ct;//num:数字 ct:个数
	int left, right; 
} node[N];
int root, p;
void insert(int &r, int val)//二叉搜索树插入
{
	if(r == 0)
	{
		int np = ++p;
		node[np].num = val;
		node[np].ct = 1;
		r = np;
		return;
	}
	if(val < node[r].num)
		insert(node[r].left, val);
	else if(val > node[r].num)
		insert(node[r].right, val);
	else
		node[r].ct++;
}
void show(int r)//中序遍历输出
{
	if(r == 0)
		return;
	show(node[r].left);
	cout << node[r].num << ' ' << node[r].ct << '\n';
	show(node[r].right);
}
int main()
{
    int n, v;
    cin >> n;
    for(int i = 1; i <= n; ++i)
    {
        cin >> v;
        insert(root, v);
    }
    show(root);
    return 0;
}

相关文章:

  • 创建位姿和显示三维物体模型
  • Walrus 经济模型 101
  • 类与对象(中)(详解)
  • 前端框架学习路径与注意事项
  • Python技术栈与数据可视化创意实践详解(三)
  • requestAnimationFrame和requestIdleCallback分别是什么,是用在什么场景下
  • ComfyUI反推提示词工作流
  • 指针:C语言的灵魂之刃(一)
  • 全面适配iOS 18.4!通付盾加固产品全面升级,护航App安全上架
  • node-red
  • NLP 面试细碎知识点 ① Transformer模型Q、K、V参数的作用
  • CI/CD(六) helm部署ingress-nginx(阿里云)
  • Netty和Project Reactor如何共同处理大数据流?
  • pytorch构建线性回归模型
  • 动捕技术革新虚拟直播:解码虚拟主播的“拟真感“破局之路
  • WEB安全--SQL注入--SQL注入的危害
  • 补Java基础之重生(13)类与对象(补充版)+面向对象综合案例
  • GPIO八种模式的应用场景总结
  • 动态规划~01背包问题
  • System.arraycopy 在音视频处理中的应用
  • 人大新闻教育70年丨16759门课程里的时代密码
  • 中美日内瓦经贸会谈联合声明
  • 湖南湘西州副州长刘冬生主动交代问题,接受审查调查
  • 中国工程院院士、国医大师石学敏逝世
  • 未来之城湖州,正在书写怎样的城市未来
  • 长期对组织隐瞒真实年龄,广元市城发集团原董事韩治成被双开