问题 H: 折半查找关键字

编写一个程序,输出一个数在长度为 n 的顺序表的从小到大的排名,并输出其在原顺序表中的下标 (下标从 1 开始)。

输入
第一行一个数 nn, 表示这个顺序表的长度 (n≤2×10 5 )。

第二行有 m 个数,表示这个顺序表中的元素,保证这些元素两两不同,但是不保证递增,注意下标从 11 开始,所有数均 m≤2×10 9

第三行一个数 tt,表示询问次数 (t≤50000)。

第四行 输出t个数,表示询问的这 t 个数。

输入输出样例
样例输入 #1
复制
8
1 3 5 6 7 10 9 8
4
1 4 8 10
样例输出 #1
复制
1 1
-1
6 8
8 6
提示
将这些元素化为有序后是 1 3 5 6 7 8 9 10。

1 的排名为 1,原下标为 1,4 不存在,8 的排名为 6原下标为 8,10 的排名为 8,原下标为 6。

二分查找的前提是序列有序,但是数据不保证递增,所以你需要先排序。

快速排序如果每次都选择当前区间的第一个元素作为基准的话效率是极不稳定的,并且有可能被卡到 O(n^2)O(n 
2
 ),应该尽量避免这种方法 (但是就本题而言,卡快速排序的数据可能并不能有效的卡掉希尔排序,并且这个测试点满足 n ≤100000.

对于输出原下标的功能,请考虑结构体。

#include<stdio.h>
#include<stdlib.h>
const int MAXN = 200010;
typedef struct 
{
    int x;
    int y;
}Node;//使用结构体用来记录顺序表的元素和元素的下标; 

// 希尔排序函数
void shell(Node arr[], int n) 
{
    int gap, i, j;
    Node tmp;
    for (gap = n/2; gap > 0; gap /= 2)//步长每次都减小一半; 
	{
        for (i = gap; i < n; i++) //第一次从gap开始往前插; 每一次处理一部分 
		{
            tmp = arr[i];//记录一下 
            for (j = i-gap; j >= 0 && arr[j].x> tmp.x; j = j-gap) 
                arr[j+gap] = arr[j];//上述条件满足的话结构体互换; 
                arr[j+gap] = tmp; 
        }
    }
}

// 二分查找函数,返回值为匹配元素的下标(从 0 开始计数)
int binary_search(Node arr[], int n, int t) {
    int left = 0, right = n-1;
    while (left <= right)
	 {
        int mid =(left+right)/2;
        if (arr[mid].x == t) 
		return mid;
        else if (arr[mid].x < t) 
		left = mid + 1;
        else right = mid - 1;
    }
    return -1;
}

int main() {
    int n, m;
    scanf("%d", &n);
    Node arr[MAXN];
    for (int i = 0; i < n; i++) 
    {
        scanf("%d", &arr[i].x);
        arr[i].y= i+1;
    }
    shell(arr, n); // 对数组 arr 进行希尔排序
    scanf("%d", &m);
    while (m>0) 
	{
		m--;
        int t;
        scanf("%d", &t);
        int pos = binary_search(arr, n, t); // 在排序后的数组 arr 中查找 t 的位置
        if (pos >= 0) printf("%d %d\n", pos + 1,arr[pos].y); // 输出 t 的在原序列中的下标和在排序后的数组 arr 中的下标
        else printf("-1\n"); // 如果没找到,则输出 -1
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值