前言 - 引言

题目:
一类有序数组旋转查值问题.
例如:
  有序数组 [ , , , , , , , , ] 旋转后为 [ , , , , , , , , ]
如何从中找出一个值索引, not found return -.

(同事面试时手写最简单一题, 回来和我说了一下, 就记下做个终结者系列)

这种旋转数组有个特点. 大家看图

相信大家豁然开朗了.  这里给个网上烂大街答案

//
// [1, 2, 3, 5, 5, 7, 7, 8, 9]
// 升序数组翻转后
// [5, 7, 7, 8, 9, 1, 2, 3, 5]
// 查找 value, return index, not found return -1;
//
int
search(int a[], int len, int v) {
int begin, end, mid;
// 异常判断
if (a == NULL || len <= )
return -; begin = ;
end = len;
while (begin < end) {
mid = (begin + end) / ;
if (a[mid] == v)
return mid; if (a[begin] < a[mid]) {
// 左边有序 [begin, mid]
if (a[begin] <= v && v < a[mid])
end = mid;
else
begin = mid + ;
} else if (a[begin] > a[mid]) {
// 右边有序 [mid, end)
if (a[mid] < v && v <= a[end - ])
begin = mid + ;
else
end = mid;
} else {
++begin;
}
} // 没有找到
return -;
}

这里使用 [begin, end) 二分法技巧. 代码支持升序旋转重复数组. 最坏情况(全重复)算法复杂度是 O(n).

不过有个问题, 如果不知道是升序 asc 还是降序 desc. 那就需要额外判断了.

// search_sort_state - 排序状态 -1 faild, 0 desc, 1 asc
static int search_sort_state(int a[], int len) {
int state, i, s[];
if (a == NULL || len <= )
return -;
// 默认 desc 降序
if (len == )
return ;
// 1, 2 asc 升序, 但必须反转为 2, 1 变成降序. 因而当前降序 desc 原本就是升序 asc
if (len == )
return a[] > a[]; // 摘取不重复的3个内容
s[] = a[]; // 开始找 s[1]
for (i = ; i < len; ++i) {
if (a[i] == s[])
continue;
break;
}
// 所有值都一样, 走默认降序
if (i >= len)
return ; s[] = a[i];
// 开始找 s[2]
while (i < len) {
if (a[i] == s[] || a[i] == s[]) {
++i;
continue;
}
break;
}
// 只有两个不一样的值, 走默认降序
if (i >= len)
return s[] > s[]; s[] = a[i]; state = ;
state += s[] > s[] ? : -;
state += s[] > s[] ? : -;
state += s[] > s[] ? : -;
return state >= ? : ;
}

最后是自己想得一个排序状态判别的算法(自我感觉巧妙). 试图找出不重复三个数. 例如

6    7    5  有

6 < 7   <

7 > 5   >

5 < 5   <

原生组合是 5   6   7

因而 < 居多是升序. > 居多是降序. (核心原因是旋转数组大小关系只改变一次)

正文 - 扩展

  有了上面铺垫那我们开始码一个问题终结者代码. 希望有所感悟

// bsearch_asc - 二分查找升序查找
static int bsearch_asc(int a[], int begin, int end, int v) {
// 简单判断
if (begin >= end || v < a[begin] || v > a[end - ])
return -; // 二分查找
do {
int mid = (begin + end) / ;
int val = a[mid]; if (val == v)
return mid; if (val < v)
begin = mid + ;
else
end = mid;
} while (begin < end); return -;
} static int search_asc(int a[], int len, int v) {
int begin = , end = len;
// 异常判断
if (begin >= end)
return -; while (begin < end) {
int mid = (begin + end) / ;
if (a[mid] == v)
return mid; if (a[begin] < a[mid]) {
// 左边有序 [begin, mid]
if (a[begin] <= v && v < a[mid])
return bsearch_asc(a, begin, mid, v);
// 右边无序, 继续循环
begin = mid + ;
} else if (a[begin] > a[mid]) {
// 右边有序 [mid, end)
if (a[mid] < v && v <= a[end - ])
return bsearch_asc(a, mid + , end, v);
// 左边无须, 继续循环
end = mid;
} else {
++begin;
}
} // 没有找到
return -;
} // bsearch_desc - 二分查找降序查找
static int bsearch_desc(int a[], int begin, int end, int v) {
// 简单判断
if (begin >= end || v > a[begin] || v < a[end - ])
return -; // 二分查找
do {
int mid = (begin + end) / ;
int val = a[mid]; if (val == v)
return mid; if (val > v)
begin = mid + ;
else
end = mid;
} while (begin < end); return -;
} static int search_desc(int a[], int len, int v) {
int begin = , end = len; while (begin < end) {
int mid = (begin + end) / ;
if (a[mid] == v)
return mid; if (a[begin] > a[mid]) {
// 左边有序 [begin, mid]
if (a[begin] >= v && v > a[mid])
return bsearch_desc(a, begin, mid, v);
// 右边无序, 继续循环
begin = mid + ;
} else if (a[begin] < a[mid]) {
// 右边有序 [mid, end)
if (a[mid] > v && v >= a[end - ])
return bsearch_desc(a, mid + , end, v);
// 左边无须, 继续循环
end = mid;
} else {
++begin;
}
} // 没有找到
return -;
}
//
// 题目:
// 一类有序数组旋转查值问题.
// 例如:
// 有序数组 [ 1, 2, 3, 5, 5, 7, 7, 8, 9 ] 旋转后为 [ 5, 7, 7, 8, 9, 1, 2, 3, 5 ]
// 如何从中找出一个值索引, not found return -1.
int
search_upgrade(int a[], int len, int v) {
int state, i, s[];
if (a == NULL || len <= )
return -;
// 默认 desc 降序
if (len == ) {
if (a[] == v)
return ;
return -;
} if (len == ) {
if (a[] == v)
return ;
if (a[] == v)
return ;
return -;
} // 摘取不重复的3个内容
s[] = a[]; // 开始找 s[1]
for (i = ; i < len; ++i) {
if (a[i] == s[])
continue;
break;
}
// 所有值都一样, 走默认降序
if (i >= len) {
if (s[] == v)
return ;
return -;
} s[] = a[state = i];
// 开始找 s[2]
while (i < len) {
if (a[i] == s[] || a[i] == s[]) {
++i;
continue;
}
break;
}
// 只有两个不一样的值, 走默认降序
if (i >= len) {
if (s[] == v)
return ;
if (s[] == v)
return state;
return -;
} s[] = a[i];
state = ;
state += s[] > s[] ? : -;
state += s[] > s[] ? : -;
state += s[] > s[] ? : -; // desc 降序, 旋转
if (state >= )
return search_desc(a, len, v);
// asc 升序, 旋转
return search_asc(a, len, v);
}

不同分支不同代码, 针对性强. 代码最坏的情况是 O(n).

这里不妨来个测试演示

#include <stdio.h>
#include <stdlib.h> #define LEN(a) (sizeof(a)/sizeof(*a)) // print - 数据内容打印
#define INT_BR (15)
static void print(int a[], int len) {
int i = ;
while (i < len) {
printf(" %d", a[i]);
if (!(++i % INT_BR))
putchar('\n');
}
if (i % INT_BR)
putchar('\n');
} int search_upgrade(int a[], int len, int v); // sort - 旋转查找
int main(int argc, char * argv[]) {
int i, v;
int a[] = { , , , , , , , , };
print(a, LEN(a));
// 开始测试
v = ;
i = search_upgrade(a, LEN(a), v);
printf("%d -> %d\n", v, i);
v = ;
i = search_upgrade(a, LEN(a), v);
printf("%d -> %d\n", v, i); v = ;
i = search_upgrade(a, LEN(a), v);
printf("%d -> %d\n", v, i); v = ;
i = search_upgrade(a, LEN(a), v);
printf("%d -> %d\n", v, i); v = ;
i = search_upgrade(a, LEN(a), v);
printf("%d -> %d\n", v, i); int b[] = { , , , , , , , , , , , , };
print(b, LEN(b));
// 开始测试
v = ;
i = search_upgrade(b, LEN(b), v);
printf("%d -> %d\n", v, i); v = ;
i = search_upgrade(b, LEN(b), v);
printf("%d -> %d\n", v, i); v = ;
i = search_upgrade(b, LEN(b), v);
printf("%d -> %d\n", v, i); v = ;
i = search_upgrade(b, LEN(b), v);
printf("%d -> %d\n", v, i); v = ;
i = search_upgrade(b, LEN(b), v);
printf("%d -> %d\n", v, i); return EXIT_SUCCESS;
}

当前输出结果如下

$ gcc -g -Wall sort.c ; ./a.out

 ->
->
-> -
->
-> - ->
->
->
->
-> -

后记 - 感谢

错误是难免的 ~ 欢迎指正 : )

小桥 - https://music.163.com/#/song?id=493042772

C基础 旋转数组查找题目的更多相关文章

  1. leetcode旋转数组查找 二分查找的变形

    http://blog.csdn.net/pickless/article/details/9191075 Suppose a sorted array is rotated at some pivo ...

  2. leetcode python 033 旋转数组查找

    ## 假设升序,import random def find(y):    l,m=len(y),0    while l>1:        n=int(l/2)        if y[0] ...

  3. (剑指Offer)面试题8:旋转数组的最小数字

    题目: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转 ...

  4. (2)剑指Offer之二维数组查找和替换空格问题

    一 二维数组查找 题目描述: 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 问 ...

  5. 剑指offer—第二章算法之二分查找(旋转数组的最小值)

    旋转数组的最小数字 题目:把一个数组最开始的若干元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素.例如:数组{3,4,5,1,2}为{1,2,3,4, ...

  6. 剑指offer 6.查找和排序 旋转数组的最小数字

    题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋 ...

  7. 28.earch in Rotated Sorted Array(排序旋转数组中查找)

    Level:   Medium 题目描述: Suppose an array sorted in ascending order is rotated at some pivot unknown to ...

  8. leetcode题解:Search in Rotated Sorted Array(旋转排序数组查找)

    题目: Suppose a sorted array is rotated at some pivot unknown to you beforehand. (i.e., 0 1 2 4 5 6 7  ...

  9. Python 迭代器&生成器,装饰器,递归,算法基础:二分查找、二维数组转换,正则表达式,作业:计算器开发

    本节大纲 迭代器&生成器 装饰器  基本装饰器 多参数装饰器 递归 算法基础:二分查找.二维数组转换 正则表达式 常用模块学习 作业:计算器开发 实现加减乘除及拓号优先级解析 用户输入 1 - ...

随机推荐

  1. Android Support Palette使用详解

    使用Palette API选择颜色 良好的视觉设计是app成功所必不可少的, 而色彩设计体系是设计的基础构成. Palette包是支持包, 能够从图片中解析出突出的颜色, 从而帮助你创建出视觉迷人的应 ...

  2. Bypass AV meterpreter免杀技巧

    0x01 meterpreter简介 MetasploitFramework是一个缓冲区溢出测试使用的辅助工具,也可以说是一个漏洞利用和测试平台,它集成了各种平台上常见的溢出漏洞和流行的shellco ...

  3. mysqlbinlog- 处理二进制日志文件的实用工具 学习笔记

    参考 MySQL 5.5官方简体中文参考手册完美版  8.6 节 调用: shell> mysqlbinlog [option] log-files... mysqlbinlog支持下面选项: ...

  4. bzoj 1814: Ural 1519 Formula 1 插头dp经典题

    用的括号序列,听说比较快. 然并不会预处理,只会每回暴力找匹配的括号. #include<iostream> #include<cstdio> #include<cstr ...

  5. 使用VS2012调试Dump文件

    前一节我讲了怎么设置C++崩溃时生成Dump文件 , 点击 传送门 , 这一节我讲讲怎么使用 VS2012 调试生成的 Dump 文件 , 甚至可以精确到出错的那一行代码上面 ; 1. 生成 Dump ...

  6. 使用 mysql-proxy 监听 mysql 查询

    什么是 mysql-proxy? mysql-proxy是mysql官方提供的mysql中间件服务,上游可接入若干个mysql-client,后端可连接若干个mysql-server. 它使用mysq ...

  7. 转:EasyJSWebView

    EasyJSWebView 是类似 Android  javascriptInterface 的 uiwebview js 调用原生代码框架 示例代码: 先建一个MyJSInterface接口 @in ...

  8. UIScrollView增加回弹效果

    项目中经常要增加上下滑动回弹的效果: self.scrollView.alwaysBounceVertical = YES;

  9. Ubuntu 搭建svn服务器 ,以及常见错误解决方案

    一.安装命令: 1)以root身份登录.执行:sudo su -命令 2)执行安装命令:apt-get install subversion   二.创建项目目录 1)mkdir  /home/svn ...

  10. ubuntu 使用小技巧

    1. 查看网速 ethstatus ubuntu下用ethstatus可以监控实时的网卡带宽占用.这个软件能显示当前网卡的 RX 和 TX 速率,单位是Byte 安装 ethstatus 软件 sud ...