python--算法

一、算法入门

第一节:概念:一个计算过程,解决问题的方法。

第二节:时间复杂度:用来评估算法运行效率

常见时间复杂度(效率排序)

O(1)<O(logn)<O(n)<O(nlogn)<O(n**2)

总结:判断时间复杂度:

  1、循环减半的过程-->O(logn)。

  2、几次循环就是n的几次方的复杂度。

第三节:空间复杂度:评估算法内存占用大小的一个式子

 第四节:二分查找和递归

  第一点:二分查找  (时间复杂度是:O(logn))

思路:按比例逐步缩小需要考虑的数据范围,从而快速的逼近需要找到的数据。

    从有序列表的候选区data[0:n]开始,通过对待查找的值与候选区中间值的比较,可以是候选区减少一半。

@cal_time
def binary_search(li, val):
    left = 0
    right = len(li) - 1
    while left <= right:  # 候选区非空
        mid = (left + right) // 2
        if li[mid] == val:
            return mid
        elif li[mid] < val:  # 更新候选区
            left = mid + 1
        else:
            right = mid - 1  # 更新候选区
    return "该值不存在"

  第二点:递归  {1.调用自己;2.有结束条件}

@cal_time
def bin_search_rec(data,val,low,high):
    if low <= high:
        mid = (low+high)  //2
        if data[mid] == val:
            return mid
        elif data[mid] > val:
            return bin_search_rec(data,val,low,mid-1)
        else:
            return bin_search_rec(data,val,mid+1,high)
    else:
        return None

 二、排序

简单排序:冒泡、选择、插入;无须列表变成有序列表。

第一章:冒泡排序

思路:后者大于前者,则将后者与前者的位置替换

@cal_time
def bubble_sort(li):
    '''
    冒泡排序,简单版
    :param li:
    :return:
    '''
    for i in range(len(li) - 1):  # i表示第i次  why减1:最后一次不用再循环
        for j in range(len(li) - i - 1):  # j表示箭头的下标
            if li[j] > li[j + 1]:  # 如果后者大于前者
                li[j], li[j + 1] = li[j + 1], li[j]  # 后者与前者替换

    return li


def bubble_sort_nb(li):
    '''
    升级版,如果前后连个数不用替换,则结束算法
    :param li:
    :return:
    '''
    for i in range(len(li)-1):
        exchange = False
        for j in range(len(li)-i-1):
            if li[j] > li[j+1]:
                li[j], li[j + 1] = li[j + 1], li[j]
                exchange = True
        if not exchange:  # 如果exchange转台没有变,则前后不用替换,结束里层循环
            break
  return li

冒泡排序时间复杂度:O(n**2)

class Node(object):
    def __init__(self, item):
        self.item = item
        self.next = None


def create_linklist_head(li):
    head = None
    for v in li:
        p = Node(v)
        p.next = head
        head = p
    return head


ll = create_linklist_head([3, 5, 2, 6, 4])


# [4, 6, 2, 5, 3]
# [4, 2, 5, 3, 6]
def buddle_sort(head):
    p = head
    flag = True
    while p.next:
        val = p.item
        next = p.next
        if val < next.item:
            flag = False
            temp = val  # 第一个值赋值给一个中间变量
            p.item = next.item  # 将后一个值赋值给前一个值
            next.item = temp  # 将前一个值后一个值
        p = next  # 改变比较的值
    if flag:
        return head
    return buddle_sort(head)


ret = buddle_sort(ll)


def print_linklist(head):
    p = head
    while p:
        print(p.item)
        p = p.next


print_linklist(ret)
链表的冒泡排序

 第二章:选择排序

如何选出列表中的最小值;正向排序与反向排序

思路:最小值默认是无序区的第一个数;下一个数如果小于前一个数;则将最小值替换。

@cal_time
def select_sort(li):
    for i in range(len(li) - 1):  # i表示次数
        min_pos = i  # 最小值默认为无序区第一个数 i
        for j in range(i + 1, len(li)):
            if li[j] < li[min_pos]:
                min_pos = j
        if min_pos != i:
            li[i], li[min_pos] = li[min_pos], li[i]

li = list(range(100))
random.shuffle(li)

select_sort(li)

最大数反向判断:  if li[j] > li[min_pos]:

 时间复杂度:O(n**2)

第三章:插入排序

思路:把列表分成有序区和无序区,最初有序区只有一个元素。

  每次从无序区选择一个元素,插入到有序去的位置,直到无序区变空。

@cal_time
def insert_sort(li):
    for i in range(1, len(li)):  # i表示摸到牌的位置 ;从1开始循环,列表里留一,就是手里的牌
        j = i - 1  # 手里牌的位置
        tmp = li[i]  # 摸到的牌
        while j >= 0 and li[j] > tmp:  # 手里有牌并且手里的牌大于摸到的牌
            li[j + 1] = li[j]
            j -= 1
        li[j + 1] = tmp  # 将摸到牌查到手里牌的后面


li = list(range(100))
random.shuffle(li)
insert_sort(li)
print(li)

 时间复杂度:O(n**2)

==================================================

 高级排序:快排、堆排、归并

第四章:快速排序   《******》

思路:

  取出一个元素p,使元素归位;

  列表被p分成两部分,左边都比p小,右边都比p大;

  递归完成排序

快排的三步骤:
     (1)在数据集之中,选择一个元素作为"基准"(pivot)。

  (2)所有小于"基准"的元素,都移到"基准"的左边;所有大于"基准"的元素,都移到"基准"的右边。

  (3)对"基准"左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。
import random
from cal_time_my import *

def partition(li, left, right):
    tmp = li[left]   # 取列表的最左数为参考点,不能写li[0],因为要分段递归
    while left < right:  # 外层大循环的条件是,左<右,至少有两个数才能循环,等于时,到了中间,大于时,错位,都应跳出循环
        while left < right and li[right] >= tmp:  # 【右循环一次】如果右边的值大于等于基值,应该留在右边,继续向左,所以right -=1;只要left=right就退出循环,所有加left<right 的条件。
            right -= 1
        li[left] = li[right]  # 右边找到小于基值tmp,就把这个数放到左边:也就是li[left] = li[right]
        while left < right and li[left] <= tmp:  # 【左循环一次】
            left += 1
        li[right] = li[left]
    li[left] = tmp  # 将最left的数写回中间位置。
    return left


def _quick_sort(li, left, right):
    '''
    递归查询
    :param li:
    :param left:
    :param right:
    :return:
    '''
    if left < right:  # 至少两个元素 递归
        mid = partition(li, left, right)
        _quick_sort(li, left, mid)  # 左到中间递归循环
        _quick_sort(li, mid + 1, right)  # 中间到右边循环

@cal_time
def quick_sort(li):
    _quick_sort(li,0,len(li)-1)

li = list(range(10000, -1, -1))
random.shuffle(li)

quick_sort(li)

javascript的快排

<script>
    function quickSort(arr, start, end) {
    if (start > end) {
        return;
    }
    let i = start,
        j = end,
        pivot = arr[start]; //存放基准数
    while (i !== j) {
        // 从右边开始,找第一个小于基准的位置
        while (arr[j] >= pivot && i < j) {
            j--;
        }
        // 从左边开始,找第一个大于基准的位置
        while (arr[i] <= pivot && i < j) {
            i++
        }
        // 交换两个数
        if (i < j) {
            let tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
        }
    }
    // 最后把基准数归位
    arr[start] = arr[i];
    arr[i] = pivot;
    // 递归处理左边
    quickSort(arr, start, i - 1);
    // 递归处理右边
    quickSort(arr, i + 1, end);
}
var arr = [6, 1, 2, 7, 9, 3, 4, 5, 10, 8];
quickSort(arr, 0, arr.length - 1);
console.log(arr);
</script>
View Code

一行实现快速排序

li=[2,3,4,1]
def qc(li): return qc([i for i in li[1:] if i <=li[0]]) + li[0:1] +qc([i for i in li[1:] if i>li[0]]) if len(li)>1 else li
res = qc(li)

时间复杂度是O(nlogn)

 

三、数据结构

 第五章 堆排序

第一节:树,一种数据结构。

树是一种可以递归定义的数据结构。

树的根节点:A

叶子节点:没有孩子的节点

树的深度(高度):就是有几层。

树的度:分了几叉;节点的度,树的度就是节点中最大的度

 孩子节点:

父节点:

 子树:子节点单独拿出来,就是一个子树

 二叉树:度不超过2的树(节点做多有两个叉)

 

 两种特殊的二叉树

满二叉树:一个二叉树,如果每一层的节点数都达到最大值,则这个二叉树就是满二叉树。

完全二叉树:叶子节点只能出现在最下层和次下层,并且最下层的节点都集中在改层最左边的若干位置的二叉树。

二叉树的存储方式

方式一:链式存储

方式二:顺序存储(列表)   二叉树使用的顺序存储。

完全二叉树可以用列表来存储,通过规律可以从父亲找到孩子或从孩子找到父亲。

孩子找父亲:

(左或右)孩子:i

父亲:(i-1)//2

----------------------------------------------

父节点和左孩子节点的编号下标关系:

父:i

左孩子:2i+1

父节点和右孩子节点的编号下标关系:

 父:i

右孩子:2i+2

 第二节:堆,一种特殊的完全二叉树

大根堆:一颗完全二叉树,满足任一节点都比其孩子节点大

小根堆:一颗完全二叉树,满足任一节点都比其孩子节点小

 堆的一个特性:向下调整性

当根节点的左右子树都是堆时,可以通过一次向下的调整来将其变换成一个堆

堆排序过程:

1.建立堆
2.得到堆顶元素,为最大元素
3.去掉堆顶,将堆最后一个元素放到堆顶,此时可通过一次调整重新使堆有序。
4.堆顶元素为第二大元素。
5.重复步骤3,直到堆变空。

 

posted @ 2018-09-27 10:15  纸上得来终觉浅,  阅读(158)  评论(0)    收藏  举报