Acient Cipher 古老的密码 快速排序法

    xiaoxiao2021-03-25  149

    UVa1339(古老的密码)

    题目:古老的密码 题目描述: 给定两个长度一样且不超过100的字符串,判断是否能把其中一个字符串的各个字母重排,之后对26个字母做一个一一映射,使得两个字符串相同 例如,JWPUDJSTVP重排后可以得到WJDUPSJPVT,之后把每个字母映射到它的前面一个字母,得到VICTORIOUS,输入两个字符串,输出YES或者NO 题目分析: 因为字母可以重排,每个字母的位置并不重要,重要的是每个字母出现的次数 ①统计两个字符串每个字母出现的次数,得到两个数组cnt1[26],cnt2[26] ②之后我们排序下,排序之后结果相同,说明我们输入的两个字符串就可以通过重排一一映射变得相同了所以本道题的核心在与【排序】 学习笔记: 关于排序 ①C语言中stdlib.h中又一个叫qsort的库函数, qsort的声明void qsort(void base, size_t num, size_t size, int(cmp)(const void , const void )); 如果排序是整型数组的话, int cmp(const void a, const void b) { return (int )a - (int )b; } ②c++中又一个函数sort更加常用些,头文件#include<algorithm> 默认sort是按升序排列,sort(a,a+n) 两个参数分别为待排序数组的首地址和尾地址 也可以写一个判定函数,按照自己的意愿排列 bool compare(const int a, const int b) { return a < b; }

    ==============================================================================

    以上是我查阅的资料,除此以外,我还深刻的理解了一下快速排序法

    资料如下:

    快速排序由于排序效率在同为O(N*logN)的几种排序方法中效率较高,因此经常被采用,再加上快速排序思想----分治法也确实实用,因此很多软件公司的笔试面试,包括像腾讯,微软等知名IT公司都喜欢考这个,还有大大小的程序方面的考试如软考,考研中也常常出现快速排序的身影。

     

    总的说来,要直接默写出快速排序还是有一定难度的,因为本人就自己的理解对快速排序作了下白话解释,希望对大家理解有帮助,达到快速排序,快速搞定。

     

    快速排序是C.R.A.Hoare1962年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)

    该方法的基本思想是:

    1.先从数列中取出一个数作为基准数。

    2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。

    3.再对左右区间重复第二步,直到各区间只有一个数。

     

    虽然快速排序称为分治法,但分治法这三个字显然无法很好的概括快速排序的全部步骤。因此我的对快速排序作了进一步的说明:挖坑填数+分治法:

    先来看实例吧,定义下面再给出(最好能用自己的话来总结定义,这样对实现代码会有帮助)。

     

    以一个数组作为示例,取区间第一个数为基准数。

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    72

    6

    57

    88

    60

    42

    83

    73

    48

    85

    初始时,i = 0;  j = 9;   X = a[i] = 72

    由于已经将a[0]中的数保存到X中,可以理解成在数组a[0]上挖了个坑,可以将其它数据填充到这来。

    j开始向前找一个比X小或等于X的数。当j=8,符合条件,将a[8]挖出再填到上一个坑a[0]中。a[0]=a[8]; i++;  这样一个坑a[0]就被搞定了,但又形成了一个新坑a[8],这怎么办了?简单,再找数字来填a[8]这个坑。这次从i开始向后找一个大于X的数,当i=3,符合条件,将a[3]挖出再填到上一个坑中a[8]=a[3]; j--;

     

    数组变为:

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    48

    6

    57

    88

    60

    42

    83

    73

    88

    85

     i = 3;   j = 7;   X=72

    再重复上面的步骤,先从后向前找,再从前向后找。

    j开始向前找,当j=5,符合条件,将a[5]挖出填到上一个坑中,a[3] = a[5]; i++;

    i开始向后找,当i=5时,由于i==j退出。

    此时,i = j = 5,而a[5]刚好又是上次挖的坑,因此将X填入a[5]

     

    数组变为:

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    48

    6

    57

    42

    60

    72

    83

    73

    88

    85

    可以看出a[5]前面的数字都小于它,a[5]后面的数字都大于它。因此再对a[0…4]a[6…9]这二个子区间重复上述步骤就可以了。

     

     

    对挖坑填数进行总结

    1.i =L; j = R; 将基准数挖出形成第一个坑a[i]

    2.j--由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。

    3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。

    4.再重复执行23二步,直到i==j,将基准数填入a[i]中。

    趁热打铁,自己实现了快速排序法:

    demo:

    void quick_sort(int s[], int l; int r) { if(l < r) { int i = l, j = r, X = s[l]; while(i < j) { while(i < j && s[j] >= X) { j--; } if(i < j) s[i++] = s[j]; while(i < j && s[i] < X) { i++; } if(i < j) s[j--] = s[i]; } s[i] = X; quick_sort(s, l, i - 1); quick_sort(s, i + 1, r); } } 扯了一大堆,本题的demo如下:

    #include <stdio.h> #include <stdlib.h> #include <string.h> #define maxn 100+10 int cmp(const void *a,const void *b) { return *(int *)a - *(int *)b; } int main() { char a[maxn]; char b[maxn]; while(scanf("%s%s",a,b) != EOF) { int len = strlen(a); int cnt1[26],cnt2[26]; memset(cnt1,0,sizeof(cnt1)); memset(cnt2,0,sizeof(cnt2)); int i; for(i = 0; i < len; i++) { cnt1[a[i] - 'A']++; cnt2[b[i] - 'B']++; } qsort(cnt1,26,sizeof(cnt1[0]),cmp); qsort(cnt2,26,sizeof(cnt2[0]),cmp); for(i = 0; i < 26; i++) { if(cnt1[i] != cnt2[i]) { printf("NO\n"); return 0; } } printf("YES\n"); } return 0; } 我自己理解这道题,最大的瓶颈是: 排序之后结果相同,说明我们输入的两个字符串就可以通过重排一一映射变得相同了

    思考加上讨论了许久,才悟出其中道理,甚为精妙啊

    转载请注明原文地址: https://ju.6miu.com/read-6072.html

    最新回复(0)