C基础 旋转数组查找题目
前言 - 引言
题目:
一类有序数组旋转查值问题.
例如:
有序数组 [ , , , , , , , , ] 旋转后为 [ , , , , , , , , ]
如何从中找出一个值索引, 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基础 旋转数组查找题目的更多相关文章
- leetcode旋转数组查找 二分查找的变形
http://blog.csdn.net/pickless/article/details/9191075 Suppose a sorted array is rotated at some pivo ...
- leetcode python 033 旋转数组查找
## 假设升序,import random def find(y): l,m=len(y),0 while l>1: n=int(l/2) if y[0] ...
- (剑指Offer)面试题8:旋转数组的最小数字
题目: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转 ...
- (2)剑指Offer之二维数组查找和替换空格问题
一 二维数组查找 题目描述: 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 问 ...
- 剑指offer—第二章算法之二分查找(旋转数组的最小值)
旋转数组的最小数字 题目:把一个数组最开始的若干元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素.例如:数组{3,4,5,1,2}为{1,2,3,4, ...
- 剑指offer 6.查找和排序 旋转数组的最小数字
题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋 ...
- 28.earch in Rotated Sorted Array(排序旋转数组中查找)
Level: Medium 题目描述: Suppose an array sorted in ascending order is rotated at some pivot unknown to ...
- 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 ...
- Python 迭代器&生成器,装饰器,递归,算法基础:二分查找、二维数组转换,正则表达式,作业:计算器开发
本节大纲 迭代器&生成器 装饰器 基本装饰器 多参数装饰器 递归 算法基础:二分查找.二维数组转换 正则表达式 常用模块学习 作业:计算器开发 实现加减乘除及拓号优先级解析 用户输入 1 - ...
随机推荐
- 【刷题】BZOJ 5418 [Noi2018]屠龙勇士
www.lydsy.com/JudgeOnline/upload/noi2018day2.pdf Solution 将攻击的式子列出来,\(atk \times x-p \times y=a_i\) ...
- 少年,想学带修改主席树吗 | BZOJ1901 带修改区间第k小
少年,想学带修改主席树吗 | BZOJ1901 带修改区间第k小 有一道题(BZOJ 1901)是这样的:n个数,m个询问,询问有两种:修改某个数/询问区间第k小. 不带修改的区间第k小用主席树很好写 ...
- Kerberos无约束委派的攻击和防御
0x00 前言简介 当Active Directory首次与Windows 2000 Server一起发布时,Microsoft就提供了一种简单的机制来支持用户通过Kerberos对Web服务器进行身 ...
- Java之使用链表实现队列
import java.util.Iterator; import java.util.NoSuchElementException; /** * 使用链表来实现队列 * 1.考虑结点的结构,包括当前 ...
- ASP.NET MVC3 入门指南之数据验证[源码RAR下载]
http://www.cnblogs.com/BingoLee/archive/2011/12/23/2298822.html 前言: 无论你编写什么样的网页程序,都需要对用户的数据进行验证,以确数据 ...
- D. Arpa and a list of numbers Codeforces Round #432 (Div. 2, based on IndiaHacks Final Round 2017)
http://codeforces.com/contest/851/problem/D 分区间操作 #include <cstdio> #include <cstdlib> # ...
- laravel mapSpread 例子
$collection = collect(range(1, 9)); $chunks = $collection->chunk(2); $labeld = $chunks->mapSpr ...
- JS--数组和字典
一.JS数组 JavaScript中的数组类似于Python的列表 a = [11,22,33,44] 常见功能: obj.length 数组的大小 obj.push(ele) 尾部追加元素 obj ...
- Docker入门与应用系列(四)网络管理
一.Docker的五种网络模式 在使用docker run创建docker容器时,可以用--net选项指定容器的网络模式,Docker有以下5种网络模式: 1. bridge模式 使用docker r ...
- LINUX 线程
1.使用进程技术的优势(1)CPU时分复用,单核心CPU可以实现宏观上的并行(2)实现多任务系统需求(多任务的需求是客观的)2.进程技术的劣势(1)进程间切换开销大(2)进程间通信麻烦而且效率低3.解 ...