二分法_ Half half

本文介绍了一种在整数数组中查找峰值的方法,并探讨了旋转排序数组中的最小值及目标值搜索策略。通过二分查找法高效定位峰值,同时解决旋转数组带来的挑战。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

无法找到一个条件形成OOXX模型

但是可以根据判断,每次只保留有解的一半,去掉无解的一半

1. Find Peak Element

你给出一个整数数组(size为n),其具有以下特点:

  • 相邻位置的数字是不同的
  • A[0] < A[1] 并且 A[n - 2] > A[n - 1]

假定P是峰值的位置则满足A[P] > A[P-1]A[P] > A[P+1],返回数组中任意一个峰值的位置。

 注意事项
  • It's guaranteed the array has at least one peak.
  • The array may contain multiple peeks, find any of them.
  • The array has at least 3 numbers in it.
样例

给出数组[1, 2, 1, 3, 4, 5, 7, 6]返回1, 即数值 2 所在位置, 或者6, 即数值 7 所在位置.

寻找峰值。


mid在数据中的分布可能有以下几种情况


当mid值在第一种情况,那么峰值一定在右侧,将start = mid即可

当mid值在第二种情况,那么峰值一定在左侧,将end = mid即可

当mid在第三种情况,那么就找到峰值。

当mid在第四种情况,那么峰值可能出现在左侧,也可能出现在右侧。

public class findPeak {
    public int findPeak(int[] A) {
        if (A == null || A.length == 0) {
        	return -1;
        }
        int results = 0;
        int start = 1;
        int end = A.length - 2;
        while (start + 1 < end) {
        	int mid = start + (end -start) / 2;
        	if (A[mid - 1] < A[mid] && A[mid] > A[mid + 1]) {
        		results = mid;
        		break;
        	} else if (A[mid - 1] < A[mid] && A[mid] < A[mid + 1]) {
        		start = mid;
        	} else if (A[mid] < A[mid - 1] && A[mid + 1] < A[mid] ) {
        		end = mid;
        	}
        }
        if (A[start - 1] < A[start] && A[start] > A[start + 1]) {
        	results = start;
        } else {
        	results = end;
        }
        return results;
    }
}


if (A == null || A.length == 0) {
    		return -1;
    	}
    	int start = 1;
    	int end = A.length - 2;
    	while (start + 1 < end) {
    		int mid = start + (end - start) / 2;
    		if (A[mid] < A[mid + 1]) {
    			start = mid;
    		} else if (A[mid] < A[mid - 1]) {
    			end = mid;
    		} else {
    			start = mid;
    		}
    	}
    	if (A[start] < A[end]) {
    		return end;
    	} else {
    		return start;
    	}
OOOXXX形式是升序的形式。

找寻找数组中的最小值

讲数组转化为OOOXXX的形式,让找到数组中第一个或最后一个满足条件的位置,OOOXXXX 前面是升序的,后面也是升序的。

旋转排序数组包括排序数组([1,2,3,4,5])

为什么以最后一个值作为参考?因为可能包含[1,2,3,4,5]这种旋转排序数组,最小值在左侧。

以最后一个值作为参考值,找到最小值的位置,如果mid大于最后一个值,那么肯定是在右侧,否则肯定在左侧。


 public int findMin(int[] nums) {
        if (nums == null || nums.length == 0) {
        	return -1;
        }
        
        int start = 0;
        int end = nums.length - 1;
        
        int target = nums[end];
        while (start + 1 < end) {
        	int mid = start + (end - start) / 2;
        	if (nums[mid] > target) {
        		start = mid;
        	} else {
        		end = mid;
        	}
        }
        return Math.min(nums[start], nums[end]);
    }

搜索旋转排序数组

在一个旋转数组中搜索目标值

记住在while 循环中不是为了找目标值,而是为了每次都能将目标值的范围缩小一半。

旋转的排序数组包含排序数组。(Rotated Sorted Array 包含 Sorted Array)

现在需要思考的是如何每次取到一个mid值就缩小一次范围?

mid会出现在上部或者在下部。

这样可以将mid出现在上部或者下部分别讨论。

当mid出现在上部时(A[mid] >= A[start]),target可能出现在两边, 当  start<=target<=mid ,那么end = mid

或者   target>=mid || target <=start  那么start = mid

当mid出现在下部时,target也可能在两边,当 mid<=target<=end ,那么start = mid

当target <= mid || target >=start 那么end = mid

public int search(int[] A, int target) {
        if(A == null || A.length == 0) {
            return -1;
        }
        int start = 0;
        int end = A.length - 1;
        int mid;
        while (start + 1 < end) {
            mid = start + (end - start) / 2;
            if (A[mid] == target) {
                return mid;
            }
            if (A[mid] >= A[start]) {
            	if (target <= A[mid] && target >= A[start]) {
            		end = mid;
            	} else {
            		start =mid;
            	}
            } else {
            	if (target >= A[mid] && target <= A[end]) {
            		start = mid;
            	} else {
            		end = mid;
            	}
            }
        }
        if (A[start] == target) {
        	return start;
        } else if (A[end] == target){
        	return end;
        } else {
            return  -1;
        }
    }



解析以下基于opencv的裂缝宽度检测算法(计算轮廓最大内切圆算法)的程序 import cv2 import math import random import numpy as np from numpy.ma import cos, sin import matplotlib.pyplot as plt def max_circle(img_path): ''' 计算轮廓内切圆算法 Args: img_path: 输入图片路径,图片需为二值化图像 Returns: ''' img = cv2.imread(img_path, cv2.IMREAD_COLOR) # 灰度处理 img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 图片二值化,缺少这一步也可以,但是图像的二值化可以使图像中数据量大为减少,从而能凸显出目标的轮廓,减小计算量 ret, thresh = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) # 寻找二值图像的轮廓 contous, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) """ 第二个参数表示轮廓的检索模式,有四种: cv2.RETR_EXTERNAL表示只检测外轮廓 cv2.RETR_LIST检测的轮廓不建立等级关系 cv2.RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。 cv2.RETR_TREE建立一个等级树结构的轮廓。 第三个参数method为轮廓的近似办法 cv2.CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1 cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息 cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法 """ expansion_circle_list = [] # 所有裂缝最大内切圆半径和圆心列表 # 可能一张图片中存在多条裂缝,对每一条裂缝进行循环计算 for c in contous: # 定义能包含此裂缝的最小矩形,矩形为水平方向 left_x = min(c[:, 0, 0]) right_x = max(c[:, 0, 0]) down_y = max(c[:, 0, 1]) up_y = min(c[:, 0, 1]) # 最小矩形中最小的边除2,裂缝内切圆的半径最大不超过此距离 upper_r = min(right_x - left_x, down_y - up_y) / 2 # 定义相切二分精度precision precision = math.sqrt((right_x - left_x) ** 2 + (down_y - up_y) ** 2) / (2 ** 13) # 构造包含轮廓的矩形的所有像素点 Nx = 2 ** 8 Ny = 2 ** 8 pixel_X = np.linspace(left_x, right_x, Nx) pixel_Y = np.linspace(up_y, down_y, Ny) # 从坐标向量中生成网格点坐标矩阵 xx, yy = np.meshgrid(pixel_X, pixel_Y) # 筛选出轮廓内所有像素点 in_list = [] for i in range(pixel_X.shape[0]): for j in range(pixel_X.shape[0]): # cv2.pointPolygonTest可查找图像中的点与轮廓之间的最短距离.当点在轮廓外时返回负值,当点在内部时返回正值,如果点在轮廓上则返回零 # 统计裂缝内的所有点的坐标 if cv2.pointPolygonTest(c, (xx[i][j], yy[i][j]), False) > 0: in_list.append((xx[i][j], yy[i][j])) in_point = np.array(in_list) # 随机搜索百分之一的像素点提高内切圆半径下限 N = len(in_point) rand_index = random.sample(range(N), N // 100) rand_index.sort() radius = 0 big_r = upper_r # 裂缝内切圆的半径最大不超过此距离 center = None for id in rand_index: tr = iterated_optimal_incircle_radius_get(c, in_point[id][0], in_point[id][1], radius, big_r, precision) if tr > radius: radius = tr center = (in_point[id][0], in_point[id][1]) # 只有半径变大才允许位置变更,否则保持之前位置不变 # 循环搜索剩余像素对应内切圆半径 loops_index = [i for i in range(N) if i not in rand_index] for id in loops_index: tr = iterated_optimal_incircle_radius_get(c, in_point[id][0], in_point[id][1], radius, big_r, precision) if tr > radius: radius = tr center = (in_point[id][0], in_point[id][1]) # 只有半径变大才允许位置变更,否则保持之前位置不变 expansion_circle_list.append([radius, center]) # 保存每条裂缝最大内切圆的半径和圆心 # 输出裂缝的最大宽度 print('裂缝宽度:', round(radius * 2, 2)) print('---------------') expansion_circle_radius_list = [i[0] for i in expansion_circle_list] # 每条裂缝最大内切圆半径列表 max_radius = max(expansion_circle_radius_list) max_center = expansion_circle_list[expansion_circle_radius_list.index(max_radius)][1] print('最大宽度:', round(max_radius * 2, 2)) # 绘制轮廓 cv2.drawContours(img_original, contous, -1, (0, 0, 255), -1) # 绘制裂缝轮廓最大内切圆 for expansion_circle in expansion_circle_list: radius_s = expansion_circle[0] center_s = expansion_circle[1] if radius_s == max_radius: # 最大内切圆,用蓝色标注 cv2.circle(img_original, (int(max_center[0]), int(max_center[1])), int(max_radius), (255, 0, 0), 2) else: # 其他内切圆,用青色标注 cv2.circle(img_original, (int(center_s[0]), int(center_s[1])), int(radius_s), (255, 245, 0), 2) cv2.imshow('Inscribed_circle', img_original) cv2.imwrite('inference/output/Inscribed_circle.png', img_original) cv2.waitKey(0) cv2.destroyAllWindows() # # matplotlib效果测试 # plot_x = np.linspace(0, 2 * math.pi, 100) # circle_X = max_center[0] + max_radius * cos(plot_x) # circle_Y = max_center[1] + max_radius * sin(plot_x) # plt.figure() # plt.imshow(img_gray) # plt.plot(circle_X, circle_Y) # plt.show() def iterated_optimal_incircle_radius_get(contous, pixelx, pixely, small_r, big_r, precision): ''' 计算轮廓内最大内切圆的半径 Args: contous: 轮廓像素点array数组 pixelx: 圆心x像素坐标 pixely: 圆心y像素坐标 small_r: 之前所有计算所求得的内切圆的最大半径,作为下次计算时的最小半径输入,只有半径变大时才允许位置变更,否则保持之前位置不变 big_r: 圆的半径最大不超过此距离 precision: 相切二分精度,采用二分法寻找最大半径 Returns: 轮廓内切圆的半径 ''' radius = small_r L = np.linspace(0, 2 * math.pi, 360) # 确定圆散点剖分数360, 720 circle_X = pixelx + radius * cos(L) circle_Y = pixely + radius * sin(L) for i in range(len(circle_Y)): if cv2.pointPolygonTest(contous, (circle_X[i], circle_Y[i]), False) < 0: # 如果圆散集有在轮廓之外的点 return 0 while big_r - small_r >= precision: # 二分法寻找最大半径 half_r = (small_r + big_r) / 2 circle_X = pixelx + half_r * cos(L) circle_Y = pixely + half_r * sin(L) if_out = False for i in range(len(circle_Y)): if cv2.pointPolygonTest(contous, (circle_X[i], circle_Y[i]), False) < 0: # 如果圆散集有在轮廓之外的点 big_r = half_r if_out = True if not if_out: small_r = half_r radius = small_r return radius if __name__ == '__main__': img_original_path = 'inference/images/crack_test1.png' img_gray = 'inference/output/test_crack.png' img_original = cv2.imread('inference/images/crack_test1.png') max_circle(img_gray)
最新发布
07-10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值