• 信息学奥赛一本通 2075:【21CSPJ普及组】插入排序(sort) | 洛谷 P7910 [CSP-J 2021] 插入排序


    【题目链接】

    ybt 2075:【21CSPJ普及组】插入排序(sort)
    洛谷 P7910 [CSP-J 2021] 插入排序

    【题目考点】

    1. 排序:
    • 插入排序
      插入排序示例:
    #include 
    using namespace std;
    int main()
    {
        int n, a[100005];
        cin >> n;
        for(int i = 1; i <= n; ++i)
            cin >> a[i];
        for(int i = 2; i <= n; ++i)//将第i个数字插入有序序列
        {
            for(int j = i; j > 1; j--)//j指向待插入数字
            {
                if(a[j] < a[j - 1])
                    swap(a[j], a[j - 1]);
                else
                    break;
            }
        }
        for(int i = 1; i <= n; ++i)
            cout << a[i] << ' ';
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 索引排序
      设原数组第i元素为a[i],经过排序后的目标数组的第i元素为t[i]
      索引数组ind:ind[i]表示t[i]在数组a中的下标。
      即有目标数组第i元素t[i]a[ind[i]]
      若要交换目标数组中两个元素swap(t[i], t[j]),索引数组的变化为swap(ind[i], ind[j])
      实际上代码中不存在目标数组t,只保存索引数组ind。对索引数组进行排序,可以在不改变原数组a的情况下得到排序后的数组。
      例:插入排序的索引排序
    #include 
    using namespace std;
    #define N 100005
    int main()
    {
        int n, a[N], ind[N];
        cin >> n;
        for(int i = 1; i <= n; ++i)
        {
    	    cin >> a[i];
        	ind[i] = i;//初始时t[i] == a[i] 
        }
    	for(int i = 2; i <= n; ++i)//将第i个数字插入有序序列
            for(int j = i; j > 1; j--)//j指向待插入数字
            {
    		    if(a[ind[j]] < a[ind[j - 1]])
                    swap(ind[j], ind[j - 1]);
                else
                    break;
            }
        for(int i = 1; i <= n; ++i)
            cout << a[ind[i]] << ' ';
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    【解题思路】

    假想存在排序后的目标数组s,设数组sa为索引数组,表示sa[i]排序后下标i的元素s[i]在数组a中的下标。也就是s[i] == a[sa[i]]
    设数组as,as[i]表示a[i]在排序后的s数组中的下标,也就是a[i] == s[as[i]]
    当i为sa[i]时,有a[sa[i]] == s[as[sa[i]]] == s[i],因此有as[sa[i]] == i
    sa与as保存的就是原数组a与目标数组s之间的两个方向的映射关系

    如果s[i]要与s[j]的值发生交换,那么就是从s[i] == a[sa[i]]同时s[j] == a[sa[j]]变为s[i] == a[sa[j]]同时s[j] == a[sa[i]],只需要交换sa[i]sa[j]的值即可。
    而后根据as[sa[i]] == i,重新设as[sa[i]]as[sa[j]]的值。因此要完成交换s[i]s[j],需要运行:

    void Swap(int i, int j)
    { 
    	swap(sa[i], sa[j]);
    	as[sa[i]] = i;
    	as[sa[j]] = j;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    主函数中,首先输入a数组,初始状态下s数组与a数组相同,满足s[i] == a[i],因此有sa[i] = i; as[i] = i;
    先根据题意,使用索引数组完成插入排序,注意交换元素时需要使用我们定义的Swap函数。
    而后进行q次操作

    • 如果要改变元素,输入x,v。先将a[x]变为v,而后观察a[x]是变大了还是变小了。

      • 如果满足v > a[x],变大了,则应该把a[x]对应在目标数组中的值s[as[x]]向后移动。
        j从as[x]开始,不断变大,向后遍历s数组,直到j为n-1。
        j向后移动的条件为:当前数字s[j]比后面的数字s[j+1]更大,或者在当前数字s[j]与后面数字s[j+1]相等的情况下,当前数字s[j]在原数组中的下标sa[j]比后面数字s[j+1]在原数组中的下标sa[j+1]更大。因为插入排序是稳定的排序,排序后相等数值元素的相对顺序不变。对于相同的数值,在原数组中下标更大的元素应该排在后面。
        只要满足这一条件,就交换s[j]s[j+1],否则结束循环。该过程中的交换操作要使用我们定义的Swap函数,实际是由索引数组sa与as完成交换。

      • 否则如果满足v <= a[x],变小了,则应该把a[x]对应在目标数组中的值s[as[x]]向前移动。
        j从as[x]开始,不断变小,向前遍历s数组,直到j为2。
        j向前移动的条件为:当前数字s[j]比前面的数字s[j-1]更小,或者在当前数字s[j]与前面数字s[j-1]相等的情况下,当前数字s[j]在原数组中的下标sa[j]比前面数字s[j-1]在原数组中的下标sa[j-1]更小。因为插入排序是稳定的排序,排序后相等数值元素的相对顺序不变。对于相同的数值,在原数组中下标更小的元素应该排在前面。
        只要满足这一条件,就交换s[j]s[j-1],否则结束循环。该过程中的交换操作要使用我们定义的Swap函数,实际是由索引数组sa与as完成交换。

    • 如果要查询原数组第x元素a[x]在s数组中的下标,就是as[x]

    【题解代码】

    解法1:
    #include
    using namespace std;
    #define N 8005
    int a[N];
    int sa[N];//sa[i]:排序后下标i的元素在数组a中的下标 
    int as[N];//as[i]:a[i]在排序后的下标 
    void Swap(int i, int j)
    {//排序后第i元素第j元素交换 
    	swap(sa[i], sa[j]);
    	as[sa[i]] = i;
    	as[sa[j]] = j;
    }
    int main()
    {
    	int n, q, t, x, v, px;
    	cin >> n >> q; 
    	for(int i = 1; i <= n; ++i)
    	{
    		cin >> a[i];
    		sa[i] = i;
    		as[i] = i;
    	}
    	for(int i = 1; i <= n; ++i)
    		for(int j = i; j >= 2; --j)
    			if(a[sa[j]] < a[sa[j-1]])
    				Swap(j, j-1);
    	for(int i = 1; i <= q; ++i)
    	{//由于插入排序是稳定的,如相等,下标大的在右边 
    		cin >> t;
    		if(t == 1)
    		{
    			cin >> x >> v;
    			if(v > a[x])//变大,a[x]右移 
    			{
    				a[x] = v;
    				for(int j = as[x]; j <= n - 1; ++j)
    				{
    					if(a[sa[j]] > a[sa[j+1]] || a[sa[j]] == a[sa[j+1]] && sa[j] > sa[j+1])
    						Swap(j, j+1);
    					else
    						break;
    				}
    			}
    			else
    			{//变小,a[x]左移
    				a[x] = v;
    				for(int j = as[x]; j >= 2; --j)
    				{
    					if(a[sa[j]] < a[sa[j-1]] || a[sa[j]] == a[sa[j-1]] && sa[j] < sa[j-1])
    						Swap(j, j-1);
    					else
    						break;
    				}
    			}
    		}
    		else
    		{
    			cin >> x;
    			cout << as[x] << endl;
    		}		
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
  • 相关阅读:
    SpringBoot+EasyExcel导入导出【加水印】
    基于Python的图书借阅管理系统,附源码
    RemObjects Suite Subscription for Delphi
    静态时序分析-配置STA环境
    C#.NET与JAVA互通之DES加密V2024
    纯前端模板文件下载如何精确控制下载的文件名字
    ​美元兑加元价格分析:空头按兵不动 寻求展开大幅整理
    Java实现常用的排序算法(快速排序、归并排序、基数排序)
    振弦采集仪应用于隧道安全监测
    嵌入式学习笔记(52)ADC的引入
  • 原文地址:https://blog.csdn.net/lq1990717/article/details/133316798