- 秒懂算法:用常识解读数据结构与算法
- (美)杰伊·温格罗
- 900字
- 2022-10-08 17:10:07
2.1 有序数组
有序数组和第1章中的“传统”数组几乎完全一致。你也能猜到,它们唯一的区别在于有序数组中的值是按顺序排列的。也就是说,插入新值时,这个值必须被放到一个合适的格子中,以免打乱数组的顺序。
以数组[3, 17, 80, 202]
为例,如下图所示。
![](https://epubservercos.yuewen.com/BDEDD8/24438780101643106/epubprivate/OEBPS/Images/29.jpg?sign=1739600111-URzJHNCjsAp2Yy0zopbZN4rnvIIEx70h-0-50285ed759b2c7bf426e363f49cd5e83)
假设要插入值75。如果这是一个传统数组,那么可以像下图这样在末尾插入75。
![](https://epubservercos.yuewen.com/BDEDD8/24438780101643106/epubprivate/OEBPS/Images/30.jpg?sign=1739600111-Fv6nnTv4QCHSlq72QXHGvtqL2kMZoWv3-0-72aeed1a5d6a937c7c2c7b07355758d3)
如第1章所述,这只需要1步。
但如果这是有序数组,那么别无选择,只能把75插入合适的位置,以保证数组的值是递增的,如下图所示。
![](https://epubservercos.yuewen.com/BDEDD8/24438780101643106/epubprivate/OEBPS/Images/31.jpg?sign=1739600111-mjO1QTHAEd5DtFJpGjR7E1wyuVzOuC3a-0-d95c74d12eac61cb153511312cc93308)
这说起来容易,做起来难。计算机无法一步到位,把75放进合适的格子。这是因为它必须先找到合适的格子,再移动其他值来腾出空间。下面来一步步分析这个过程。
首先是原来的有序数组,如下图所示。
![](https://epubservercos.yuewen.com/BDEDD8/24438780101643106/epubprivate/OEBPS/Images/32.jpg?sign=1739600111-FjKzhWyYMTi4zph0ohld9OdyorCMytHT-0-eb31890d5ce00b28e41dabe7b471c882)
第1步:检查索引0处的值来确定要在它前面还是后面插入新值75,如下图所示。
![](https://epubservercos.yuewen.com/BDEDD8/24438780101643106/epubprivate/OEBPS/Images/33.jpg?sign=1739600111-uM9q95tsMvq6CvhT328LJtzfC2G0B5P6-0-16fb79c5099ac6d2dfab27107f0f0a2a)
因为75比3大,所以必须插到它右边。不过,因为依然不知道具体位置,所以必须检查下一个格子。
这样的步骤叫作比较。我们会比较要插入的值和有序数组中现有的值。
第2步:检查下一个格子的值,如下图所示。
![](https://epubservercos.yuewen.com/BDEDD8/24438780101643106/epubprivate/OEBPS/Images/34.jpg?sign=1739600111-LHPFjwa7BCqP3EdQvyWsTXnS8fT1raLJ-0-ae4c8ff6cf7f633e242d377671a5f994)
75比17大,所以还得继续右移。
第3步:检查下一个格子的值,如下图所示。
![](https://epubservercos.yuewen.com/BDEDD8/24438780101643106/epubprivate/OEBPS/Images/35.jpg?sign=1739600111-DLPNTUA9geIohM6jtx9duAvP0j3VeBqe-0-75e2a6578cddeb3edb38ec79f2bdf4f8)
这次的值是80,比要插入的75大。因为我们碰到了第一个比75大的值,所以得出结论:要保证有序数组的有序性,75必须紧挨着放在80的左边。为此,需要右移数据为75腾出空间。
第4步:把最后一个值右移,如下图所示。
![](https://epubservercos.yuewen.com/BDEDD8/24438780101643106/epubprivate/OEBPS/Images/36.jpg?sign=1739600111-JVIehd9oH3qR52EaeLEeEmcufFFzZsDc-0-e0c0a14e35c0c30bfb7d338cb79fc7ae)
第5步:把倒数第二个值右移,如下图所示。
![](https://epubservercos.yuewen.com/BDEDD8/24438780101643106/epubprivate/OEBPS/Images/37.jpg?sign=1739600111-SEvOwyDY1mW3oWPPxuI1CgPyc0SmGngz-0-a313441ba915e446d8ed1e77db6d2b65)
第6步:把75插到正确的位置,如下图所示。
![](https://epubservercos.yuewen.com/BDEDD8/24438780101643106/epubprivate/OEBPS/Images/38.jpg?sign=1739600111-jX5J0ZZH8VD6DpktuGKMyUs5DQsMQWm3-0-a64ba883dce73ad284ab35f52c3fc7b7)
可以看出,当向有序数组插入元素时,总是需要在插入前查找正确的插入位置。这就是传统数组和有序数组在性能上的差别之一。
在这个例子中,数组有4个元素,插入用了6步。而对于包含N个元素的有序数组,插入则需要花N+2步。
有趣的是,在有序数组中,无论新值最后插到哪里,所需的步骤数都差不多。如果这个值最后位于数组开头,那么所需的比较就更少,移动就更多。如果它最后位于数组末尾,那么比较就更多,移动就更少。当新值位于数组的最末尾时,因为不需要移动任何数据,所以总共需要的步骤数就最少。在此情况下,和N个现有的值比较需要N步,而插入本身还需要1步,因此,共计为N+1步。
虽然有序数组的插入比传统数组慢,但其查找则另有玄机。