返回顶部

3 排序与搜索

排序与搜索

排序算法(英语:Sorting algorithm)是一种能将一串数据依照特定顺序进行排列的一种算法。

排序算法的稳定性

稳定性:稳定排序算法会让原本有相等键值的纪录维持相对次序。也就是如果一个排序算法是稳定的,当有两个相等键值的纪录R和S,且在原本的列表中R出现在S之前,在排序过的列表中R也将会是在S之前。

冒泡排序

冒泡排序(英语:Bubble Sort)是一种简单的排序算法。它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

冒泡排序算法的运作如下:

  • 比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。
  • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  • 针对所有的元素重复以上的步骤,除了最后一个。
  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
复制代码
def bubble_sort(li):
    n = len(li)
    # 总共排序几趟 ,n - 1 目的是为了空间排序的次数
    for j in range(n-1):  # j = 0 1 2 3
        count = 0
        # 排序一趟,找到一个最大的数,排到最右边
        for i in range(n-1-j): # 排序的次数 越来越小
            # 比较相邻的两个数

            if li[i] > li[i + 1]:
                li[i], li[i + 1] = li[i + 1], li[i]
                count += 1
        if count == 0:
            break

if __name__ == '__main__':
    li = [1,2,3,4,5]
    bubble_sort(li)
    print(li)
    # 最坏时间复杂度 O(n^2)
    # 最优时间复杂度 O(n)
    # 稳定性 稳定
复制代码

选择排序

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def select_sort(li):
    n = len(li)
    # 让temp 从 0 ~ n-2 每次循环,选择一个最小的数,替换到j位置
    for j in range(n-1):
 
        # 把0位置的数据,跟后面所有的数进行比较
        # 比出来最小的数,放到0位置
        # 把0位置角标记录,跟后面所有的数进行比较
        temp = j
        for i in range(j + 1, n):
            if li[i] < li[temp]:
                # 记录最小的数的角标
                temp = i
        # for循环结束后,temp是最小的数的角标
        li[temp], li[j] = li[j], li[temp]
 
 
if __name__ == '__main__':
    li = [3,3,5,2,1]
    select_sort(li)
    print(li)
    # 最坏时间复杂度:O(n^2)
    # 最优时间复杂度:O(n^2)
    # 稳定性:不稳定 [3,3,5,2,1]

插入排序

插入排序(英语:Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

 

复制代码
def insert_sort(li):
    n = len(li)
    for j in range(1,n):
        # 把1位置到n位置的数,依次进行插入排序
        # 一次插入,把前面的数据认为有序,把当前位置的数据插入到前面的有序序列中
        for i in range(j, 0 , -1):
            if li[i] < li[i - 1]:
                li[i], li[i - 1] = li[i - 1], li[i]
            else:
                break

if __name__ == '__main__':
    li = [3,1,5,2,6]
    insert_sort(li)
    print(li)
    # 最坏时间复杂度:O(n^2)
    # 最优时间复杂度:O(n)
    # 稳定性:稳定
复制代码

快速排序

快速排序(英语:Quicksort),又称划分交换排序(partition-exchange sort),通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

步骤为:

  1. 从数列中挑出一个元素,称为"基准"(pivot),
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。

快速排序的分析

复制代码
def quick_sort(alist, first, last):
    """快速排序"""
    if first >= last:
        return
    mid_value = alist[first]
    low = first
    high = last
    while low < high:
        # high 左移
        while low < high and alist[high] >= mid_value:
            high -= 1
        alist[low] = alist[high]

        while low <high and alist[low] < mid_value:
            low += 1
        alist[high] = alist[low]
    # 从循环退出时,low==high
    alist[low] = mid_value

    # 对low左边的列表执行快速排序
    quick_sort(alist, first, low-1)

    # 对low右边的列表排序
    quick_sort(alist, low+1, last)


if __name__ == "__main__":
    li = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    print(li)
    quick_sort(li, 0, len(li)-1)
    print(li)
复制代码

二分查找 

复制代码
# 主要的思想是从中间开始,大的网右边查找,小的往左边查找,依次递归

def search(li,item):
    if len(li) == 0:
        return False
    mid = len(li)//2
    if item == mid:
        return True
    if item > li[mid]:
        return search(li[mid+1:],item)
    else:
        return search(li[:mid,item])


if __name__ == '__main__':
    li = [1,2,3,4,5]
    print(search(li,2))
    print(search(li,6))
View Code
复制代码

队列 

复制代码
class Queue(object):
    def __init__(self):
        self.items = []

    def enqueue(self, item):
        """往队列中添加一个item元素"""
        self.items.insert(0, item)

    def dequeue(self):
        """从队列头部删除一个元素"""
        return self.items.pop()

    def is_empty(self):
        """判断一个队列是否为空"""
        return self.items == []

    def size(self):
        """返回队列的大小"""
        return len(self.items)

if __name__ == '__main__':
    queue = Queue()
    queue.enqueue(1)
    queue.enqueue(2)
    queue.enqueue(3)
    print(queue.size())
    print(queue.is_empty())
    print(queue.dequeue())
    print(queue.dequeue())
    print(queue.dequeue())
View Code
复制代码

 

双端队列

复制代码
class Dequeue(object):
    def __init__(self):
        self.items = []

    def add_front(self, item):
        """从队头加入一个item元素"""
        self.items.insert(0, item)

    def add_rear(self, item):
        """从队尾加入一个item元素"""
        self.items.append(item)

    def remove_front(self):
        """从队头删除一个item元素"""
        return self.items.pop(0)

    def remove_rear(self):
        """从队尾删除一个item元素"""
        return self.items.pop()

    def is_empty(self):
        """判断双端队列是否为空"""
        return self.items == []

    def size(self):
        """返回队列的大小"""
        return len(self.items)

if __name__ == '__main__':
    dequeue = Dequeue()
    # 当成栈
    # dequeue.add_front(1)
    # dequeue.add_front(2)
    # print(dequeue.remove_front())
    # print(dequeue.remove_front())

    # dequeue.add_rear(3)
    # dequeue.add_rear(4)
    # print(dequeue.remove_rear())
    # print(dequeue.remove_rear())

    # 当成队列
    # dequeue.add_front(1)
    # dequeue.add_front(2)
    #
    # print(dequeue.remove_rear())
    # print(dequeue.remove_rear())

    dequeue.add_rear(3)
    dequeue.add_rear(4)
    print(dequeue.remove_front())
    print(dequeue.remove_front())
View Code
复制代码

 

复制代码
class Stack(object):

    def __init__(self):
        # 初始化一个列表
        self.items = []

    def push(self, item):
        """添加一个新的元素item到栈顶"""
        # 从尾部添加 时间复杂度是O(1)
        self.items.append(item)

    def pop(self):
        """弹出栈顶元素"""
        # 从尾部弹出,时间复杂度是O(1)
        return self.items.pop()

    def peek(self):
        """返回栈顶元素"""
        # 列表的尾部是栈顶
        return self.items[self.size()-1]

    def is_empty(self):
        """判断栈是否为空"""
        return self.items == []

    def size(self):
        """返回栈的元素个数"""
        return len(self.items)

if __name__ == '__main__':
    stack = Stack()
    print(stack.is_empty())
    stack.push(1)
    stack.push(2)
    stack.push(3)
    print(stack.is_empty())
    print(stack.size())
    print(stack.pop())
    print(stack.peek())
    print(stack.pop())
    print(stack.pop())
View Code
复制代码

单向链表 

复制代码
class Node(object):
    """单链表的节点类"""
    # 初始化变量
    def __init__(self, item):
        # 记录节点数据的变量
        self.item = item
        # 记录下一个节点
        self.next = None


class SingleLinkList(object):
    """单链表类"""
    def __init__(self):
        # 所有的操作都需要首节点
        self.__head = None

    def is_empty(self):
        """链表是否为空"""
        # 判断当前链表中的头节点是否为空
        return self.__head is None

    def length(self):
        """链表长度"""
        # 定义变量记录节点个数
        count = 0
        # 创建游标变量,记录首节点
        cur = self.__head
        # 循环让游标变量遍历节点
        while cur is not None:
            # 游标指向了一个节点,节点个数+1
            count += 1
            # 让游标往后移动,指向下一个节点
            cur = cur.next
        # 返回节点的总数
        return count

    def travel(self):
        """遍历整个链表"""
        cur = self.__head
        while cur is not None:
            # 当前游标指向了一个节点,输出它的数据内容
            print(cur.item,end=' ')
            cur = cur.next
        # 遍历完成,换行
        print()

    def add(self, item):
        """链表头部添加元素"""
        # 创建节点
        node = Node(item)
        # 让创建的节点记录之前的头结点
        node.next = self.__head
        # 把创建的这个节点赋值给head变量
        self.__head = node

    def append(self, item):
        """链表尾部添加元素"""
        # 判断链表是否为空
        if self.is_empty():
            # 调用头部添加节点
            self.add(item)
            return
        # 1、遍历链表,找到尾节点
        cur = self.__head
        # 循环完成后,cur指向尾节点
        while cur.next is not None:
            cur = cur.next

        # 2、让尾节点的next指向新节点
        node = Node(item)
        cur.next = node

    def insert(self, pos, item):
        """指定位置添加元素"""
        # 根据传入的位置判断
        if pos <= 0:
            # 添加到首节点
            self.add(item)
        elif pos >= self.length():
            # 添加到尾部
            self.append(item)
        else:
            # 1、需要先找到插入位置的前一个节点
            # 2、把新节点的next指向之前位置的节点
            # 3、让找到前一个节点指向新节点
            cur = self.__head
            # 记录当前游标的索引
            index = 0
            # 遍历链表,结束时,游标指向指定位置的前一个节点
            while index < pos -1:
                cur = cur.next
                index += 1
            node = Node(item)
            node.next = cur.next
            cur.next = node

    def remove(self, item):
        """删除节点"""
        # 1、遍历找到要删除的元素
        # 2、找到要删除节点的前一个节点
        # 3、删除节点
        cur = self.__head
        # pre记录cur游标的前一个节点
        pre = None
        while cur is not None:
            # 判断当前游标指向的节点是否是要删除的节点
            if cur.item == item:
                # 删除当前游标指向的节点
                # 如果删除的是首节点,pre为None
                if pre is None:
                    self.__head = cur.next
                else:
                    pre.next = cur.next
            # 当cur游标往后面移动之前,先把cur指向的节点赋值给pre
            pre = cur
            cur = cur.next

    def search(self, item):
        """查找节点是否存在"""
        # 遍历链表
        cur = self.__head
        while cur is not None:
            # 判断当前节点的内容
            if cur.item == item:
                return True
            cur = cur.next
        return False


if __name__ == '__main__':
    sll = SingleLinkList()
    print(sll.is_empty())
    print(sll.length())
    sll.add(5)
    sll.add(2)
    sll.add(1)
    print(sll.length())
    sll.travel()
    sll.append(3)
    sll.travel()
    sll.insert(2,4)
    sll.travel()
    print(sll.search(3))
    print(sll.search(6))
    sll.remove(5)
    sll.travel()
View Code
复制代码

 

双向链表

复制代码
class Node(object):
    """双向链表的节点类"""
    # 初始化变量
    def __init__(self, item):
        # 记录节点数据的变量
        self.item = item
        # 记录上一个节点
        self.pre = None
        # 记录下一个节点
        self.next = None


class DoubleLinkList(object):
    """双向链表类"""
    def __init__(self):
        # 所有的操作都需要首节点
        self.__head = None

    def is_empty(self):
        """链表是否为空"""
        # 判断当前链表中的头节点是否为空
        return self.__head is None

    def length(self):
        """链表长度"""
        # 定义变量记录节点个数
        count = 0
        # 创建游标变量,记录首节点
        cur = self.__head
        # 循环让游标变量遍历节点
        while cur is not None:
            # 游标指向了一个节点,节点个数+1
            count += 1
            # 让游标往后移动,指向下一个节点
            cur = cur.next
        # 返回节点的总数
        return count

    def travel(self):
        """遍历整个链表"""
        cur = self.__head
        while cur is not None:
            # 当前游标指向了一个节点,输出它的数据内容
            print(cur.item,end=' ')
            cur = cur.next
        # 遍历完成,换行
        print()

    def add(self, item):
        """链表头部添加元素"""
        # 创建节点
        node = Node(item)
        # 判断链表是否为空
        if self.is_empty():
            self.__head = node
            return

        # 让老的头节点的pre记录新的节点
        self.__head.pre = node
        # 让创建的节点记录之前的头结点
        node.next = self.__head
        # 把创建的这个节点赋值给head变量
        self.__head = node

    def append(self, item):
        """链表尾部添加元素"""
        # 判断链表是否为空
        if self.is_empty():
            # 调用头部添加节点
            self.add(item)
            return
        # 1、遍历链表,找到尾节点
        cur = self.__head
        # 循环完成后,cur指向尾节点
        while cur.next is not None:
            cur = cur.next

        # 2、让尾节点的next指向新节点
        node = Node(item)
        cur.next = node
        # 让新的尾节点的pre指向老的尾节点
        node.pre = cur

    def insert(self, pos, item):
        """指定位置添加元素"""
        # 根据传入的位置判断
        if pos <= 0:
            # 添加到首节点
            self.add(item)
        elif pos >= self.length():
            # 添加到尾部
            self.append(item)
        else:
            # 1、需要先找到插入位置的前一个节点
            # 2、把新节点的next指向之前位置的节点
            # 3、让找到前一个节点指向新节点
            cur = self.__head
            # 记录当前游标的索引
            index = 0
            # 遍历链表,结束时,游标指向指定位置的前一个节点
            while index < pos -1:
                cur = cur.next
                index += 1
            node = Node(item)
            node.next = cur.next
            # 老位置的节点的pre指向新节点
            cur.next.pre = node
            cur.next = node
            # 新节点的pre指向它的前一个节点
            node.pre = cur

    def remove(self, item):
        """删除节点"""
        # 1、遍历找到要删除的元素
        # 2、找到要删除节点的前一个节点
        # 3、删除节点
        cur = self.__head
        # pre记录cur游标的前一个节点
        while cur is not None:
            # 判断当前游标指向的节点是否是要删除的节点
            if cur.item == item:
                # 删除当前游标指向的节点
                # 如果删除的是首节点,pre为None
                if cur.pre is None:
                    self.__head = cur.next
                else:
                    cur.pre.next = cur.next
                    # 如果删除的不是尾节点
                    if cur.next is not None:
                        cur.next.pre = cur.pre
            # 当cur游标往后面移动之前,先把cur指向的节点赋值给pre
            cur = cur.next

    def search(self, item):
        """查找节点是否存在"""
        # 遍历链表
        cur = self.__head
        while cur is not None:
            # 判断当前节点的内容
            if cur.item == item:
                return True
            cur = cur.next
        return False


if __name__ == '__main__':
    sll = DoubleLinkList()
    print(sll.is_empty())
    print(sll.length())
    sll.add(5)
    sll.add(2)
    sll.add(1)
    print(sll.length())
    sll.travel()
    sll.append(3)
    sll.travel()
    sll.insert(2,4)
    sll.travel()
    print(sll.search(3))
    print(sll.search(6))
    sll.remove(5)
    sll.travel()
View Code
复制代码

 

单向循环链表

复制代码
class Node(object):
    """单循环链表的节点类"""
    # 初始化变量
    def __init__(self, item):
        # 记录节点数据的变量
        self.item = item
        # 记录下一个节点
        self.next = None


class SingleCycLinkList(object):
    """单循环链表类"""
    def __init__(self):
        # 所有的操作都需要首节点
        self.__head = None

    def is_empty(self):
        """链表是否为空"""
        # 判断当前链表中的头节点是否为空
        return self.__head is None

    def length(self):
        """链表长度"""
        # 判断是否为空
        if self.is_empty():
            return 0
        # 定义变量记录节点个数
        count = 1
        # 创建游标变量,记录首节点
        cur = self.__head
        # 循环让游标变量遍历节点
        while cur.next is not self.__head:
            # 游标指向了一个节点,节点个数+1
            count += 1
            # 让游标往后移动,指向下一个节点
            cur = cur.next
        # 返回节点的总数
        return count

    def travel(self):
        """遍历整个链表"""
        # 判断是否为空
        if self.is_empty():
            return

        cur = self.__head
        # 打印首节点
        print(cur.item, end=' ')
        while cur.next is not self.__head:
            # 当前游标指向了一个节点,输出它的数据内容
            cur = cur.next
            print(cur.item,end=' ')
        # 遍历完成,换行
        print()

    def add(self, item):
        """链表头部添加元素"""
        # 创建节点
        node = Node(item)
        # 判断是否为空
        if self.is_empty():
            self.__head = node
            # 自己的next指向自己
            node.next = node
            return

        # 遍历找到尾节点,让尾节点指向新的头
        cur = self.__head
        while cur.next is not self.__head:
            cur = cur.next
        # 当while循环结束,cur指向的就是尾节点


        # 让创建的节点记录之前的头结点
        node.next = self.__head
        # 把创建的这个节点赋值给head变量
        self.__head = node
        # 让尾节点指向新的头节点
        cur.next = self.__head

    def append(self, item):
        """链表尾部添加元素"""
        # 判断链表是否为空
        if self.is_empty():
            # 调用头部添加节点
            self.add(item)
            return
        # 1、遍历链表,找到尾节点
        cur = self.__head
        # 循环完成后,cur指向尾节点
        while cur.next is not self.__head:
            cur = cur.next

        # 2、让尾节点的next指向新节点
        node = Node(item)
        cur.next = node
        # 新节点指向头节点
        node.next = self.__head

    def insert(self, pos, item):
        """指定位置添加元素"""
        # 根据传入的位置判断
        if pos <= 0:
            # 添加到首节点
            self.add(item)
        elif pos >= self.length():
            # 添加到尾部
            self.append(item)
        else:
            # 1、需要先找到插入位置的前一个节点
            # 2、把新节点的next指向之前位置的节点
            # 3、让找到前一个节点指向新节点
            cur = self.__head
            # 记录当前游标的索引
            index = 0
            # 遍历链表,结束时,游标指向指定位置的前一个节点
            while index < pos -1:
                cur = cur.next
                index += 1
            node = Node(item)
            node.next = cur.next
            cur.next = node

    def remove(self, item):
        """删除节点"""
        # 判断为空
        if self.is_empty():
            return
        # 1、遍历找到要删除的元素
        # 2、找到要删除节点的前一个节点
        # 3、删除节点
        cur = self.__head
        # pre记录cur游标的前一个节点
        pre = None
        while cur.next is not self.__head: # 处理不了尾节点
            # 判断当前游标指向的节点是否是要删除的节点
            if cur.item == item:
                # 删除当前游标指向的节点
                # 如果删除的是首节点,pre为None
                if pre is None:
                    # 找到尾节点
                    temp = self.__head
                    while temp.next is not self.__head:
                        temp = temp.next
                    self.__head = cur.next
                    temp.next = self.__head
                else:
                    pre.next = cur.next

            # 当cur游标往后面移动之前,先把cur指向的节点赋值给pre
            pre = cur
            cur = cur.next
        # 结束时判断尾节点是否是要删除的节点
        if cur.item == item:
            # 当pre为空时,证明当前只有一个节点
            if pre is None:
                self.__head = None
            else:
                pre.next = self.__head

    def search(self, item):
        """查找节点是否存在"""
        # 判断是否为空
        if self.is_empty():
            return False
        # 遍历链表
        cur = self.__head
        # 判断首节点是否是要找的节点
        if cur.item == item:
            return True
        while cur.next is not self.__head:
            # 判断当前节点的内容
            cur = cur.next
            if cur.item == item:
                return True
        return False


if __name__ == '__main__':
    sll = SingleCycLinkList()
    print(sll.is_empty())
    print(sll.length())
    sll.add(5)
    sll.add(2)
    sll.add(1)
    print(sll.length())
    sll.travel()
    sll.append(3)
    sll.travel()
    sll.insert(2,4)
    sll.travel()
    print(sll.search(3))
    print(sll.search(1))
    print(sll.search(6))
    sll.remove(5)
    sll.remove(3)
    sll.travel()
View Code
复制代码

 

posted @   Crazymagic  阅读(152)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示

目录导航