【算法图解】 读书笔记:第2张 选择排序

287 阅读3分钟

数组和链表


内存就想是一个个排列的抽屉,每一个抽屉都是一个内存单元。

需要将数据存储到内存时,你请求计算机提供存储空间,计算机给你一个存储地址。需要存 储多项数据时,有两种基本方式——数组和链表。

数组在内存中的空间需要连续性。假设你要为数组分配1000个位置,内存中有1000个位置,但不都靠在一起,这种情况下,你将无法为该数组分配内存。而链表只要有足够的内存空间,就能为链表分配内存,链表的每个元素都存储了下一个元素的地址,从而使一系列随机的内存地址串在一起。

数组和链表的优劣势


如果一个数组包含五个元素,起始地址为00,那么第五个元素的地址是多少呢?显而易见是04。需要随机读取元素是,数组的效率很高,可以迅速定位。

而链表随机读取元素时,需要从第一个元素开始读取,查找到下一个元素的内存地址……,一直到找到我们需要的元素,效率相对数组就比较底下了。那么链表更时候在什么场景使用呢?

当我们需要进行插入、删除操作时,数组可能会遇到内存不足的情况,这个时候数组需要将整个数组复制到其他地方进行插入操作,插入的时候,后续的数组需要每一个都往后面移一位,为插入的值腾出内存空间,一看就是一个很麻烦的操作。

这个时候就是链表的优势体现的时候,链表只需要将上一个元素的指向下一个内存空间的地址绑定成它的就完成了操作。

具体我们看看常见数组和链表操作的运行时间

数组 读取O(1) 插入O(n) 删除O(n)

链表 读取O(n) 插入O(1) 删除O(n)

可见,数组更适合读取,链表更适合插入与删除操作。

选择排序


例:有一个值为数字的乱序数组,你该如何为它排序呢?

我们就可以使用选择排序来解决这个问题,即,从数组内遍历出最大值,加入新数组,将最大值从原数组中删除,重复上述操作,最后得出的新数组就是一个从大到小排序的数组了。

我们简单看一下这个算法的大O表示法,第一次需要执行n次,找出最大值,第二次执行(n-1)次,找出最大值……,一共需要重复n次,所以需要的时间就是O(n^2)。相对速度是比较慢的。

JavaScript实现:

function getArrayMaxIndex(arr) {
  let max = arr[0];
  let maxIndex = 0;
  arr.forEach((current, index) => {
    if(current > max) {
      max = current;
      maxIndex = index;
    }
  });
  return maxIndex;
}

function selectSort(arr) {
  const newArr = [];
  const length = arr.length;
  for(let i = 0; i < length; i++) {
    const maxIndex = getArrayMaxIndex(arr);
    newArr.push.apply(newArr, arr.splice(maxIndex, 1));
  }
  return newArr;
}

console.log(selectSort([10,22,98,37,-193,392,28,284,278]));
// [ 392, 284, 278, 98, 37, 28, 22, 10, -193 ]

python实现:

def get_list_max_index(list):
  max = list[0]
  max_index = 0
  for i in range(1, len(list)):
    if(max < list[i]):
      max = list[i]
      max_index = i
  return max_index

def select_sort(list):
  new_list = []
  for i in range(len(list)):
    max_index = get_list_max_index(list)
    new_list.append(list.pop(max_index))
  return new_list

print(select_sort([10,22,98,37,-193,392,28,284,278]))

小结


  • 计算机内存犹如一大堆抽屉。
  • 需要存储多个元素时,可使用数组或链表。
  • 数组的元素都在一起。
  • 链表的元素是分开的,其中每个元素都存储了下一个元素的地址。
  • 数组的读取速度很快。
  • 链表的插入和删除速度很快。