数据结构和算法 字符串匹配
![](https://i-blog.csdnimg.cn/blog_migrate/b2500a4dc03489fe85d47676b6be724d.png)
在我之前的文章: 算法和数据结构面试准备和演练—第1部分中 ,我们讨论了如何进行复杂性时空分析,并通过示例介绍了常见的Big-O因素。
在这篇文章中,我将开始深入讨论Array,并涵盖一些采访问题,并希望在阅读结束时,您对Array有一个很好的了解。 一旦我们熟悉Array,我将讨论String以及如何使用Array解决String问题。
数组是一种包含一组元素的数据结构 。 数组的最基本实现是静态数组。 之所以称为静态,是因为大小是固定的。 对特定位置的读/写访问为O(1)。
静态数组的实现
class StaticArray
def initialize(length)
self.store = Array.new(length)
end
# O(1)
def [](index)
self.store[index]
end
# O(1)
def []=(index, value)
self.store[index] = value
end
protected
attr_accessor :store
end
我们从静态数组创建动态数组,如下所示。 读/写访问也是O(1)。 让我们为其实现一些通用方法,例如pop(),push(),shift()和unshift()。 这里的关键是,当我们达到数组大小时,我们想要调整其大小并将其空间加倍,以便将新元素push()或unshift()移到数组中。
动态数组的实现
require_relative "static_array"
class DynamicArray
attr_reader :length
# O(1)
def [](index)
check_index(index)
@store [index]
end
# O(1)
def []=(index, value)
check_index(index)
@store [index] = value
end
# O(1) amortized ; O(n) worst case.
def push(val)
resize! if @length == @capacity
@store [ @length + 1] = val
@length += 1
end
# O(n): has to shift over all the elements.
def shift
check_index(0)
idx = 0
first_el = @store [0]
while idx < @length - 1
@store [idx] = @store [idx + 1]
idx += 1
end
@length -= 1
first_el
end
# O(n): has to shift over all the elements.
def unshift(val)
resize! if @length == @capacity
idx = @length
while idx > 0
@store [idx] = @store [idx - 1]
idx -= 1
end
@store [0] = val
@length += 1
@store
end
protected
attr_accessor :capacity, :store
attr_writer :length
def check_index(index)
raise "out of bounds" if ( @length < index + 1 || index < 0)
end
# O(n): has to copy over all the elements to the new store.
def resize!
new_store = StaticArray.new( @capacity * 2)
idx = 0
while idx < @length
new_store[idx] = @store [idx]
idx += 1
end
@store = new_store
@capacity *= 2
end
end
什么是摊销?
如果您仔细阅读,会发现代码片段中有一个关键字“摊销”。 那是什么意思? 当我们想将新元素追加(或推送)到数组并达到其大小限制时,我们希望将大小增加一倍。 但是,请resize!
方法分配更大的区域,移动整个数组,并删除前一个数组。 这是O(n)
运算。 但是,如果我们仅每O(1/n)
次执行一次,则平均而言,它仍然可以得出O(n * 1/n) = O(1)
。 这就是所谓的摊销成本 。
动态数组的时间复杂度和空间复杂度
在平均和最坏的情况下,
Access O(1)
Search O(n)
Insertion O(n)
(在Array的末尾是O(1)摊销,在Array的开始或中间是O(n)
Deletion O(n)
Space O(n)
现在,我们知道访问Array中的元素的速度很快( O(1)
),而搜索/添加/删除操作则相对较慢( O(n)
),这有时需要遍历整个数组。
环形缓冲区
它是一种使用静态数组的数据结构,就好像它是端对端连接的一样。
require_relative "static_array"
class RingBuffer
attr_reader :length
def initialize
@length = 0
@capacity = 8
@start_idx = 0
@store = StaticArray.new( @capacity )
end
# O(1)
def [](index)
check_index(index)
ring_index = (index + @start_idx ) % @capacity
@store [ring_index]
end
# O(1)
def []=(index, val)
check_index(index)
ring_index = (index + @start_idx ) % @capacity
@store [ring_index] = val
end
# O(1)
def pop
check_index(0)
@length -= 1;
val = @store [( @length + @start_idx ) % @capacity ]
@store [( @length + @start_idx ) % @capacity ] = nil
val
end
# O(1) amortized
def push(val)
resize! if @length == @capacity
@store [( @length + @start_idx ) % @capacity ] = val
@length += 1
end
# O(1)
def shift
check_index(0)
val = @store [ @start_idx ]
@store [ @start_idx ] = nil
@start_idx = ( @start_idx + 1) % @capacity
@length -= 1
val
end
# O(1) amortized
def unshift(val)
resize! if @length == @capacity
@start_idx = ( @start_idx - 1) % @capacity
@store [ @start_idx ] = val
@length += 1
val
end
protected
attr_accessor :capacity, :start_idx, :store
attr_writer :length
def check_index(index)
raise "index out of bounds" if (index < 0 || index > @length - 1)
end
def resize!
new_store = StaticArray.new( @capacity * 2)
idx = 0
while idx < @length
new_store[idx] = @store [( @start_idx + idx) % @capacity ]
idx += 1
end
@store = new_store
@start_idx = 0
@capacity *= 2
end
end
要掌握Array数据结构和问题,我们至少需要非常熟悉:
- 循环操作。
- 使用指针记录位置的感觉。
- 交换技术。
- 基本数学
- 常见的数组方法及其时间复杂度,例如pop(),push(),shift(),unshift(),forEach(),sort(),slice(),splice(),reverse(),concat() ,filter(),map()等等。
使用数组的一些优点:
●恒定时间访问并允许随机访问
●分组类似项目
还有一些缺点...
●对于大型阵列,插入和删除可能会很昂贵
●动态数组需要调整大小,并且受分配的大小限制
足够说了,这是一些针对您的练习的热门面试问题:
- 移动零-给定一个数字数组,编写一个函数,将所有
0
移到其结尾,同时保持非零元素的相对顺序。 ( 思考过程和解决方案 ) - 股票101 —何时购买/出售股票? 给定一个数组,其中包含股票的每日价格。 尝试找到最大的利润。 ( 思考过程和解决方案 )
- 查找重复项—给定一个数字数组,如果任何数字在数组中出现多次,则返回true,否则返回false。 ( 思考过程和解决方案 )
串
现在我们了解了Array,下面我们来谈谈String,它只是基于字符的Array 。 您只需要学习解决数组问题的技术即可,并且天生就是一名String Master!
在深入研究与String相关的问题之前,我们需要熟悉以下内容:
- 与字符串相关的方法,即charAt(),include(),trim(),concat(),slice(),split(),substring(),toUpperCase(),toLowerCase(),toString()等。
- 两个指针。
- 在数组中交换元素。
- 递归。
在我的下一篇文章中 ,我将讨论队列,堆栈和链接列表的数据结构。
你走之前 -
没有比在Medium( Victor Lin )上追随我更好的支持我的方法了。 让我知道我应该写更多!
您知道按下down按钮最多可以放弃50?吗? 如果您再次喜欢这篇文章,请尝试一下!
数据结构和算法 字符串匹配