74. Search a 2D Matrix (M)

本文介绍了一种高效的算法,用于在行元素和列元素均递增的矩阵中查找特定值。通过两次二分查找或直接将矩阵视为一维数组进行二分查找,实现了快速定位目标值。提供了两种代码实现方式。

Search a 2D Matrix (M)

Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:

  • Integers in each row are sorted from left to right.
  • The first integer of each row is greater than the last integer of the previous row.

Example 1:

Input:
matrix = [
  [1,   3,  5,  7],
  [10, 11, 16, 20],
  [23, 30, 34, 50]
]
target = 3
Output: true

Example 2:

Input:
matrix = [
  [1,   3,  5,  7],
  [10, 11, 16, 20],
  [23, 30, 34, 50]
]
target = 13
Output: false

题意

判断在一个行元素递增、列元素也递增的矩阵中能否找到目标值。

思路

有序序列中找值那肯定是要用二分法,不过可以有两种二分查找的方式:

  1. 先一个二分找到目标值应该在的行,再一个二分在该行中查找目标值。
  2. 由于矩阵的特殊性(行递增,且下一行元素都比上一行元素大),可以将矩阵直接当成长度为 m*n 的一维数组处理。

代码实现 - 矩阵二分

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length;
        if (m == 0) return false;
        int n = matrix[0].length;
        if (n == 0) return false;

        if (target < matrix[0][0]) {
            return false;
        }

        // 先找行
        int left = 0, right = m - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (target < matrix[mid][0]) {
                right = mid - 1;
            } else if (target > matrix[mid][0]) {
                left = mid + 1;
            } else {
                return true;
            }
        }
        int r = left - 1;

        // 再找列
        left = 0;
        right = n - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (target < matrix[r][mid]) {
                right = mid - 1;
            } else if (target > matrix[r][mid]) {
                left = mid + 1;
            } else {
                return true;
            }
        }
        
        return false;
    }
}

代码实现 - 一维二分

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length;
        if (m == 0) return false;
        int n = matrix[0].length;
        if (n == 0) return false;

        int left = 0, right = m * n - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            // 一维坐标转换为二维坐标
            int x = mid / n;
            int y = mid % n;
            if (target < matrix[x][y]) {
                right = mid - 1;
            } else if (target > matrix[x][y]) {
                left = mid + 1;
            } else {
                return true;
            }
        }
        return false;
    }
}
import numpy as np import scipy from sklearn.linear_model import LinearRegression import matplotlib.pyplot as plt from matplotlib.widgets import Slider, Button import copy import re import time from common import framer from irtools import IRv from visuals import ToggleText, SliderGroup, slider_guard class feedback_delay_network: def __init__(self, fs, m, A_type, A_gain, b, c, d = 1, A = None): self.fs = fs self.m = np.array(m, dtype=np.int64) self.A_type = A_type self.A_gain = A_gain self.b = np.array(b) self.c = np.array(c) self.d = d self.A = self.factory_A(A_type, self.m.size) * self.A_gain def get_config_dict(self): return dict(fs=self.fs, m=self.m, A_type=self.A_type, A_gain=self.A_gain, b=self.b, c=self.c, d=self.d) def _filt(self, x, zi): if len(x) > min(self.m): raise ValueError('Block size of FDN shall not exceed any delay length.') y = self.d * x + self.c.dot(zi[0][:, :len(x)]) ss = self.A.dot(zi[0][:, :len(x)]) + np.transpose([self.b]).dot([x]) zi[0] = np.hstack([zi[0][:, len(x):], np.zeros_like(zi[0][:, :len(x)])]) for i, m in enumerate(self.m): zi[0][i, m - len(x) : m] = ss[i] return y, [zi[0], zi[1]] def filt(self, x, zi): if zi[0].shape != (len(self.m), max(self.m)): zi = self.make_z(zi) y = np.zeros_like(x) for block, pos in framer(x, min(self.m), min(self.m), ret_pos=True): y1, zi = self._filt(block, zi) y[pos : pos + len(block)] = y1 return y, zi def filter_1(self, x): zi = self.make_z() return self.filt(x, zi)[0] def filter(self, x): s = np.zeros((len(self.m), len(x) + np.max(self.m))) for n, xn in enumerate(x): ss = self.A.dot(s[:, n]) + xn * self.b # feedback s[range(len(self.m)), n + self.m] = ss # delay y = self.d * x + self.c.dot(s[:, :len(x)]) return y def make_z(self, zi = None): # print('make_z', self.m) new_z = np.zeros((len(self.m), max(self.m))) if zi != None: old_z, old_m = zi for i in range(min(len(self.m), len(old_m))): copy_z = min(self.m[i], old_m[i]) new_z[i][self.m[i] - copy_z : self.m[i]] = old_z[i][old_m[i] - copy_z : old_m[i]] return [new_z, self.m] def set_A(self): self.A = self.factory_A(self.A_type, self.m.size) * self.A_gain def set_A_gain(self, A_gain): if self.A_gain != A_gain: self.A_gain = A_gain self.set_A() def set_A_type(self, A_type): if self.A_type != A_type: self.A_type = A_type self.set_A() def set_order(self, M): if M < self.m.size: self.m = self.m[:M] self.b = self.b[:M] self.c = self.c[:M] self.set_A() elif M > self.m.size: self.m = self.extend_m(self.m, M) self.b = self.extend_repeat(self.b, M) self.c = self.extend_repeat(self.c, M) self.set_A() def short_desc(self): return [f'{self.fs}Hz', self.A_type + str(self.m.size) + f'x{self.A_gain:.3g}', f'm: {min(self.m)}~{max(self.m)}'] @staticmethod def extend_repeat(m, M): new_m = np.zeros(M) new_m[:m.size] = m new_m[m.size:] = m[-1] return new_m @staticmethod def extend_m(m, M): new_m = np.zeros(M, dtype=np.int64) new_m[:m.size] = m new_m[m.size:] = np.rint(m[-1] * 1.216589714650669 ** np.arange(1, M - m.size + 1)).astype(np.int64) return new_m @staticmethod def factory_A(A_type, order = None): if order is not None: type_str = A_type else: r = re.search('([A-Za-z]+)([0-9]+)', A_type) type_str = r.group(1) order = int(r.group(2)) if type_str == 'Stautner': A = feedback_delay_network.Stautner_A(order) elif type_str == 'Hadamard': A = feedback_delay_network.Hadamard_A(order) else: raise ValueError('FDN: unrecognized feedback matrix type \'' + A_type + '\'') return A @staticmethod def Stautner_A(order = 4): A = np.array([[0., 1, 1, 0], [-1, 0, 0, -1], [1, 0, 0, -1], [0, 1, -1, 0]]) / np.sqrt(2) if order == 4: return A A = np.kron(A, rot_matrix(np.pi/4)) if order == 8: return A A = np.kron(A, rot_matrix(np.pi/8)) if order == 16: return A @staticmethod def Hadamard_A(order = 4): return scipy.linalg.hadamard(order) / np.sqrt(order) def rot_matrix(theta): return np.array([[np.cos(theta/2), -np.sin(theta/2)], [np.sin(theta/2), np.cos(theta/2)]]) # FDN form is used to specify an FDN. It provides several configuration inputs for FDN parameters, # as well as visual and audio support for examining the impulse response. # The expected use scenario is: # . an external caller opens this form, supplying an external fdn instance # . the form's contents (fdn settings) are populated by those of the external fdn # . the user interacts with the form, changes the paramters and examines the resultant fdn # . the user applys the changes with Apply or OK buttons, or discards them with Cancel button # . the form is closed with OK or cancel buttons class FDNForm: # parent class IRv provides visual / audio support for examining the impulse response def __init__(self, fdn: feedback_delay_network, auto_apply = False, on_apply = None): # set up form layout fig, ax = plt.subplot_mosaic('AA;BC', height_ratios=[1, 4]) fig.subplots_adjust(left=0.05, right=0.95) fig.subplots_adjust(top=0.95, bottom=0.5) self.figure = fig self.canvas = fig.canvas # axes 'A' is used as a panel to host several FDN controls ax['A'].set_axis_off() # FDN control: feedback matrix type self.Stautner_Toggle = ToggleText(-0.04, 0.2, 'S', ax['A'], on_action=self.on_type_toggle, tag='Stautner') self.Hadamard_Toggle = ToggleText(-0.01, 0.2, 'H', ax['A'], on_action=self.on_type_toggle, tag='Hadamard') # FDN control: number of delay lines self.Order4_Toggle = ToggleText(0.09, 0.2, '4', ax['A'], on_action=self.on_type_toggle, tag=4) self.Order8_Toggle = ToggleText(0.12, 0.2, '8', ax['A'], on_action=self.on_type_toggle, tag=8) self.Order16_Toggle = ToggleText(0.15, 0.2, '16', ax['A'], on_action=self.on_type_toggle, tag=16) # axes 'B' and 'C' are passed to IRv # super().__init__(ax['B'], ax['C']) self.irv = IRv(ax['B'], ax['C']) # IRv does not provide a textual label, so we add it here. ir_ax is from parent class IRv self.ir_label = self.irv.ir_ax.axes.text(0.01, 1.01, '', transform=self.irv.ir_ax.axes.transAxes, fontsize = 'x-small', picker=1, va='bottom', wrap=True) # FDN controls: feedback gain self.gain_slider = Slider(fig.add_axes([0.10, 0.92, 0.8, 0.04]), label="FB gain", valmin=0, valmax=1.0, valinit=0.95, valstep=0.005) self.gain_slider.on_changed(self.on_gain_slider_change) # FDN control: delays, forward gains and output gains self.Mgroup = SliderGroup(self.figure, [0.05, 0.1, 0.3, 0.3], [], valmin=1, valmax=1000, valstep=1, label='Delays', on_change=self.on_M_change, sizeup=0.4, sizemm=0.15, allow_alt=True) self.Bgroup = SliderGroup(self.figure, [0.4, 0.1, 0.25, 0.3], [], label='B', on_change=self.on_B_change, allow_alt=True) self.Cgroup = SliderGroup(self.figure, [0.7, 0.1, 0.25, 0.3], [], label='C', on_change=self.on_C_change, allow_alt=True) # apply/ok/cancel buttons self.ApplyButton = Button(fig.add_axes([0.65, 0.03, 0.1, 0.05]), 'Apply') self.ApplyButton.on_clicked(self.ApplyClick) self.OkButton = Button(fig.add_axes([0.75, 0.03, 0.1, 0.05]), 'OK') self.OkButton.on_clicked(self.OkClick) self.CancelButton = Button(fig.add_axes([0.85, 0.03, 0.1, 0.05]), 'Cancel') self.CancelButton.on_clicked(self.CancelClick) self.auto_apply = auto_apply self.on_apply = on_apply # fig.canvas.mpl_connect('pick_event', self._pick) self.set_fdn(fdn, init=True) # set feedback matrix (by type or size), triggered by ToggleText buttons def on_type_toggle(self, sender : ToggleText): if sender in [self.Stautner_Toggle, self.Hadamard_Toggle]: # # This shows how one creates a new FDN to replace the old # self.set_fdn(feedback_delay_network(**{**self.fdn.get_config_dict(), 'A_type':sender.tag})) # This shows how one updates the imcumbent FDN self.fdn.set_A_type(sender.tag) self.set_fdn() elif sender in [self.Order4_Toggle, self.Order8_Toggle, self.Order16_Toggle]: self.fdn.set_order(sender.tag) self.set_fdn() def on_gain_slider_change(self, val): self.fdn.set_A_gain(val) self.set_fdn() def on_B_change(self, sg : SliderGroup, sender : Slider): self.fdn.b[sender.index] = sender.val self.set_fdn() def on_C_change(self, sg : SliderGroup, sender : Slider): self.fdn.c[sender.index] = sender.val self.set_fdn() def on_M_change(self, sg : SliderGroup, sender : Slider): self.fdn.m[sender.index] = sender.val self.set_fdn() def set_fdn(self, fdn : feedback_delay_network = None, init = False): # set_fdn(...) may be called with or without the fdn argument. # .when called with an external fdn, it keeps a reference of the external fdn and makes a # copy of it as the internal fdn. Form contents are repopulated to reflect the setup # of the new internal fdn. # .when called without fdn argument, it is assumed that the interal fdn is modified by user. # Form contents are updated to synchronize with that change. if fdn: self.ref_fdn = fdn self.fdn = copy.deepcopy(fdn) if not hasattr(self, 'fs') or self.fs != fdn.fs: self.irv.set_fs(fdn.fs) # IRv uses a filterbank that needs the sample rate self.ir_label.set_text((['FDN '] + self.fdn.short_desc())) # textual description of current (internal) FDN self.Stautner_Toggle.set_on(self.fdn.A_type == 'Stautner') # type of FDN feedback matrix self.Hadamard_Toggle.set_on(self.fdn.A_type == 'Hadamard') self.Order4_Toggle.set_on(self.fdn.m.size == 4) # size of FDN feedback matrix (number of delay lines) self.Order8_Toggle.set_on(self.fdn.m.size == 8) self.Order16_Toggle.set_on(self.fdn.m.size == 16) with slider_guard(self.gain_slider) as gs: gs.set_val(self.fdn.A_gain) # FDN feedback gain self.Mgroup.set_data(self.fdn.m, init=init) # FDN delays self.Bgroup.set_data(self.fdn.b, init=init) # FDN forward gains self.Cgroup.set_data(self.fdn.c, init=init) # FDN output gains # compute and show IR, RT M = max(self.fdn.m) L = M * 2 zi = self.fdn.make_z() self.ir, zi = self.fdn.filt(scipy.signal.unit_impulse(L, idx=1), zi) e0 = np.sum(self.ir ** 2) / 2 while self.ir.size < self.fdn.fs * 10: ir, zi = self.fdn.filt(np.zeros(M), zi) self.ir = np.concatenate([self.ir, ir]) e = np.sum(ir ** 2) e0 = max(e0, e) if e < e0 * 1e-6: break # reserve extra length for delays in analysis filters L_ex = np.ceil(max(self.irv.octave_filter_bank.delays)).astype(np.int64) ir_ex = self.fdn.filt(np.zeros(L_ex), zi)[0] self.ir = np.c_[self.ir] # present ir self.irv.put_ir(self.ir[:, 0], ir_ex) if self.auto_apply: self._apply() def _apply(self): _tmp_fdn = copy.deepcopy(self.fdn) self.ref_fdn.__dict__.update(_tmp_fdn.__dict__) if self.on_apply: self.on_apply(self) def ApplyClick(self, event): self._apply() def CancelClick(self, event): plt.close(self.figure) def OkClick(self, event): self.ApplyClick(event) self.CancelClick(event) def main(): fs = 48000 # n = feedback_delay_network(fs, [149, 211, 263, 293], 'Stautner4', 1 / sqrt(6.2), np.ones(4), np.ones(4) * .8, 1) fdn_N = 4 fdn_m = [149, 211, 263, 293, 331, 347, 391, 433, 513, 696, 837, 1003, 1245, 1449, 1838, 2257] n = feedback_delay_network(fs, fdn_m[:fdn_N], 'Stautner', 0.95, np.ones_like(fdn_m[:fdn_N]) * 0.5, np.ones_like(fdn_m[:fdn_N]) * .8, 1) dsf = FDNForm(n) plt.show() dsf = FDNForm(n) plt.show() return 这个代码文件的功能是什么
12-18
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值