经常使用排序

 

 

 

1、几种排序的比较:

 

2、排序的代码实现

 

一、冒泡排序

 给定一组随机数字的数列,将它们按照从小到大的顺序从新排列好。就像冒泡同样,小的数不断地向上漂浮,直到没有为止。html

排序基本思路:循环这个数列,将循环到的数字n1与下一个数字n2做出对比,若是n2>n1,那么将两个值换位,如此下去当第一次循环结束时,最小的数已经在最前面,重复这样的循环就会将这组数按从小到大的顺序排列好。node

li = [11,432,5,4576,234,3,324,5,876,456,235432,56]

for m in range(len(li)-1):
    for n in range(m+1, len(li)):
        if li[m] > li[n]:
            tmp = li[m]
            li[m] = li[n]
            li[n] = tmp

print li

测试排列5万个小于2000的随机数字,结果发现用时为:118秒python

 

二、选择排序

与冒泡排序相似,循环整个数组,将数组中的值与第一个数值作对比,并将较小的值放在第一位。第一次循环就能够找出最小的一个值,以此类推下去,知道所有排列好。git

 

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import random,time


def selection_sort(array):
    for i in range(len(array)):
        for j in range(i, len(array)):
            if array[i] > array[j]:
                tmp = array[i]
                array[i] = array[j]
                array[j] = tmp


if __name__ == '__main__':
    # array = [871,100,160,755,614,621,403,671,256,915,174,906]
    array = []
    for i in range(50000):
        array.append(random.randrange(100000))


    time_start = time.time()
    selection_sort(array)
    time_end = time.time()
    ret = time_end-time_start
    print array
    print(ret)

 

测试排列5万个小于2000的随机数字,结果发现用时为:115秒算法

选择排序的优化数据库

当咱们在循环中,判断出后面的值与第一个值的大小,那么先不作出交换,仅仅记录下这个较小值的下标。当第一次循环完时才把最小值放置在最前面,这样交换的次数更少了,速度就更快乐了。数组

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import random,time


def selection_sort(array):
    for i in range(len(array)):
        for j in range(i, len(array)):
            if array[i] > array[j]:
                tmp = array[i]
                array[i] = array[j]
                array[j] = tmp


if __name__ == '__main__':
    # array = [871,100,160,755,614,621,403,671,256,915,174,906]
    array = []
    for i in range(50000):
        array.append(random.randrange(2000))


    time_start = time.time()
    selection_sort(array)
    time_end = time.time()
    ret = time_end-time_start
    # print array
    print(ret)

测试排列5万个小于2000的随机数字,结果发现用时为:105数据结构

 

 

三、插入排序(Insertion Sort)

排序基本思路:插入排序简单的理解为  从一组数据的第二个值n开始循环,经过n与前面数值对比大小,将n插入到前面合适的位置。经过不断地向后循环对比大小,那么前面一部分的数据始终是已经排序好的数组。这样所有循环下来,就获得了一个从小到大排列OK的数组。app

[77, 92, 67, 8, 6, 84, 55, 85, 43, 67]
[67, 77, 92, 8, 6, 84, 55, 85, 43, 67]
[8, 67, 77, 92, 6, 84, 55, 85, 43, 67]
[6, 8, 67, 77, 92, 84, 55, 85, 43, 67]
[6, 8, 67, 77, 84, 92, 55, 85, 43, 67]
[6, 8, 55, 67, 77, 84, 92, 85, 43, 67]
[6, 8, 55, 67, 77, 84, 85, 92, 43, 67]
[6, 8, 43, 55, 67, 77, 84, 85, 92, 67]
[6, 8, 43, 55, 67, 67, 77, 84, 85, 92]dom

 

# _*_coding:utf:8_*_

import random import time def sort_meth(source): for i in range(1, len(source)): currant_val = source[i]  # 获取当前循环列表中的值
        position = i    # 获取当前循环的次数
        while position > 0 and source[position-1] > currant_val:  # 当左边的数大于大循环中的数时
 source[position] = source[position-1]  # 将小循环此刻的数赋值等于左边较大的值
            position -= 1 source[position] = currant_val    # 最终小循环结束时,就是大循环的数应该插入的位置


if __name__ == '__main__': #array = [871,100,160,755,614,621,403,671,256,915,174,906]
    array = [] for i in range(50000): array.append(random.randrange(2000)) time_start = time.time() sort_meth(array) time_end = time.time() ret = time_end-time_start print(ret)
插入排序

测试排列5万个小于2000的随机数字,结果发现用时为:115

 

四、快速排序(quick sort)

 设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(一般选用数组的第一个数)做为关键数据,而后将全部比它小的数都放到它前面,全部比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变更


注:在待排序的文件中,若存在多个关键字相同的记录,通过排序后这些具备相同关键字的记录之间的相对次序保持不变,该排序方法是稳定的;若具备相同关键字的记录之间的相对次序发生改变,则称这种排序方法是不稳定的
要注意的是,排序算法的稳定性是针对全部输入实例而言的。即在全部可能的输入实例中,只要有一个实例使得算法不知足稳定性要求,则该排序算法就是不稳定的。 

 

排序演示

假设用户输入了以下数组:
下标
0
1
2
3
4
5
数据
6
2
7
3
8
9
建立变量i=0(指向第一个数据), j=5(指向最后一个数据), k=6(赋值为第一个数据的值)。
咱们要把全部比k小的数移动到k的左面,因此咱们能够开始寻找比6小的数,从j开始,从右往左找,不断递减变量j的值,咱们找到第一个下标3的数据比6小,因而把数据3移到下标0的位置,把下标0的数据6移到下标3,完成第一次比较:
下标
0
1
2
3
4
5
数据
3
2
7
6
8
9
i=0 j=3 k=6
接着,开始第二次比较,此次要变成找比k大的了,并且要从前日后找了。递加变量i,发现下标2的数据是第一个比k大的,因而用下标2的数据7和j指向的下标3的数据的6作交换,数据状态变成下表:
下标
0
1
2
3
4
5
数据
3
2
6
7
8
9
i=2 j=3 k=6
称上面两次比较为一个循环。
接着,再递减变量j,不断重复进行上面的循环比较。
在本例中,咱们进行一次循环,就发现i和j“碰头”了:他们都指向了下标2。因而,第一遍比较结束。获得结果以下,凡是k(=6)左边的数都比它小,凡是k右边的数都比它大:
下标
0
1
2
3
4
5
数据
3
2
6
7
8
9
若是i和j没有碰头的话,就递加i找大的,尚未,就再递减j找小的,如此反复,不断循环。注意判断和寻找是同时进行的。
 
此时,数值k就将整个数组分割了左右两份。分别对这左右两份的数据再次执行上述的过程,直到不能分割为止。
注意:第一遍快速排序不会直接获得最终结果,只会把比k大和比k小的数分到k的两边。为了获得最后结果,须要再次对下标2两边的数组分别执行此步骤,而后再分解数组,直到数组不能再分解为止(只有一个数据),才能获得正确结果。

 

# _*_coding:utf-8_*_


import random
import time


def quick_sort(array, start, end):
    if start >= end:
        return

    left = start
    right = end
    value = array[left]

    while left < right:

        while left < right and array[right] > value:
            right -= 1

        array[left] = array[right]
        array[right] = value

        while left < right and array[left] <= value:
            left += 1

        array[right] = array[left]
        array[left] = value
    quick_sort(array, start, left-1)
    quick_sort(array, left+1, end)


if __name__ == '__main__':

    # array = [871,100,160,755,614,621,403,671,256,915,174,906]
    array = []
    for i in range(50000):
        array.append(random.randrange(2000))

    time_start = time.time()
    quick_sort(array, 0, len(array)-1)
    time_end = time.time()
    ret = time_end-time_start
    print(ret)

 

 测试排列5万个小于2000的随机数字,结果发现用时为:0.233秒,

当看到这个结果,其实我是惊呆了!

 

 

五、二叉树

树的特征和定义


  树是一种重要的非线性数据结构,直观地看,它是数据元素(在树中称为结点)按分支关系组织起来的结构,很象天然界中的树那样。树结构在客观世界中普遍存在,如人类社会的族谱和各类社会组织机构均可用树形象表示。树在计算机领域中也获得普遍应用,如在编译源程序时,可用树表示源程序的语法结构。又如在数据库系统中,树型结构也是信息的重要组织形式之一。一切具备层次关系的问题均可用树来描述。
 

树(Tree)是元素的集合。咱们先以比较直观的方式介绍树。下面的数据结构是一个树:

树有多个节点(node),用以储存元素。某些节点之间存在必定的关系,用连线表示,连线称为边(edge)。边的上端节点称为父节点,下端称为子节点。树像是一个不断分叉的树根。

每一个节点能够有多个子节点(children),而该节点是相应子节点的父节点(parent)。好比说,3,5是6的子节点,6是3,5的父节点;1,8,7是3的子节点, 3是1,8,7的父节点。树有一个没有父节点的节点,称为根节点(root),如图中的6。没有子节点的节点称为叶节点(leaf),好比图中的1,8,9,5节点。从图中还能够看到,上面的树总共有4个层次,6位于第一层,9位于第四层。树中节点的最大层次被称为深度。也就是说,该树的深度(depth)为4。

 

若是咱们从节点3开始向下看,而忽略其它部分。那么咱们看到的是一个以节点3为根节点的树:

三角形表明一棵树

再进一步,若是咱们定义孤立的一个节点也是一棵树的话,原来的树就能够表示为根节点和子树(subtree)的关系:

 

上述观察实际上给了咱们一种严格的定义树的方法:

1. 树是元素的集合。

2. 该集合能够为空。这时树中没有元素,咱们称树为空树 (empty tree)。

3. 若是该集合不为空,那么该集合有一个根节点,以及0个或者多个子树。根节点与它的子树的根节点用一个边(edge)相连。

上面的第三点是以递归的方式来定义树,也就是在定义树的过程当中使用了树自身(子树)。因为树的递归特征,许多树相关的操做也能够方便的使用递归实现。咱们将在后面看到。

 

树的实现

树的示意图已经给出了树的一种内存实现方式: 每一个节点储存元素和多个指向子节点的指针。然而,子节点数目是不肯定的。一个父节点可能有大量的子节点,而另外一个父节点可能只有一个子节点,而树的增删节点操做会让子节点的数目发生进一步的变化。这种不肯定性就可能带来大量的内存相关操做,而且容易形成内存的浪费。

一种经典的实现方式以下:

树的内存实现

拥有同一父节点的两个节点互为兄弟节点(sibling)。上图的实现方式中,每一个节点包含有一个指针指向第一个子节点,并有另外一个指针指向它的下一个兄弟节点。这样,咱们就能够用统一的、肯定的结构来表示每一个节点。

 

计算机的文件系统是树的结构,好比Linux文件管理背景知识中所介绍的。在UNIX的文件系统中,每一个文件(文件夹一样是一种文件),均可以看作是一个节点。非文件夹的文件被储存在叶节点。文件夹中有指向父节点和子节点的指针(在UNIX中,文件夹还包含一个指向自身的指针,这与咱们上面见到的树有所区别)。在git中,也有相似的树状结构,用以表达整个文件系统的版本变化 (参考版本管理三国志)。

 
 

二叉树 

二叉树是由n(n≥0)个结点组成的有限集合、每一个结点最多有两个子树的有序树。它或者是空集,或者是由一个根和称为左、右子树的两个不相交的二叉树组成。

特色:

(1)二叉树是有序树,即便只有一个子树,也必须区分左、右子树;

(2)二叉树的每一个结点的度不能大于2,只能取0、一、2三者之一;

(3)二叉树中全部结点的形态有5种:空结点、无左右子树的结点、只有左子树的结点、只有右子树的结点和具备左右子树的结点。

 

二叉树(binary)是一种特殊的树。二叉树的每一个节点最多只能有2个子节点:

二叉树

因为二叉树的子节点数目肯定,因此能够直接采用上图方式在内存中实现。每一个节点有一个左子节点(left children)和右子节点(right children)。左子节点是左子树的根节点,右子节点是右子树的根节点。

 

若是咱们给二叉树加一个额外的条件,就能够获得一种被称做二叉搜索树(binary search tree)的特殊二叉树。二叉搜索树要求:每一个节点都不比它左子树的任意元素小,并且不比它的右子树的任意元素大。

(若是咱们假设树中没有重复的元素,那么上述要求能够写成:每一个节点比它左子树的任意节点大,并且比它右子树的任意节点小)

二叉搜索树,注意树中元素的大小

二叉搜索树能够方便的实现搜索算法。在搜索元素x的时候,咱们能够将x和根节点比较:

1. 若是x等于根节点,那么找到x,中止搜索 (终止条件)

2. 若是x小于根节点,那么搜索左子树

3. 若是x大于根节点,那么搜索右子树

二叉搜索树所须要进行的操做次数最多与树的深度相等。n个节点的二叉搜索树的深度最多为n,最少为log(n)。

 

二叉树的遍历

遍历即将树的全部结点访问且仅访问一次。按照根节点位置的不一样分为前序遍历,中序遍历,后序遍历。

前序遍历:根节点->左子树->右子树

中序遍历:左子树->根节点->右子树

后序遍历:左子树->右子树->根节点

例如:求下面树的三种遍历

 

前序遍历:abdefgc

中序遍历:debgfac

后序遍历:edgfbca

 

二叉树的类型

(1)彻底二叉树——若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,而且叶子结点都是从左到右依次排布,这就是彻底二叉树
(2)满二叉树——除了叶结点外每个结点都有左右子叶且叶子结点都处在最底层的二叉树。
(3)平衡二叉树——平衡二叉树又被称为AVL树(区别于AVL算法),它是一棵二叉排序树,且具备如下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,而且左右两个子树都是一棵平衡二叉树

如何判断一棵树是彻底二叉树?按照定义,

教材上的说法:一个深度为k,节点个数为 2^k - 1 的二叉树为满二叉树。这个概念很好理解,

就是一棵树,深度为k,而且没有空位。

首先对满二叉树按照广度优先遍历(从左到右)的顺序进行编号。

一颗深度为k二叉树,有n个节点,而后,也对这棵树进行编号,若是全部的编号都和满二叉树对应,那么这棵树是彻底二叉树。

 

image

 

 

如何判断平衡二叉树?

2008111712242127

(b)左边的图 左子数的高度为3,右子树的高度为1,相差超过1

(b)右边的图 -2的左子树高度为0  右子树的高度为2,相差超过1

 

二叉树遍历实现

class TreeNode(object):
    def __init__(self,data=0,left=0,right=0):
        self.data = data
        self.left = left
        self.right = right
 
class BTree(object):
    def __init__(self,root=0):
        self.root = root
 
 
    def preOrder(self,treenode):
        if treenode is 0:
            return
        print(treenode.data)
        self.preOrder(treenode.left)
        self.preOrder(treenode.right)
    def inOrder(self,treenode):
        if treenode is 0:
            return
        self.inOrder(treenode.left)
        print(treenode.data)
        self.inOrder(treenode.right)
 
    def postOrder(self,treenode):
        if treenode is 0:
            return
        self.postOrder(treenode.left)
        self.postOrder(treenode.right)
        print(treenode.data)
if __name__ == '__main__':
    n1  = TreeNode(data=1)
    n2 = TreeNode(2,n1,0)
    n3 = TreeNode(3)
    n4 = TreeNode(4)
    n5 = TreeNode(5,n3,n4)
    n6 = TreeNode(6,n2,n5)
    n7 = TreeNode(7,n6,0)
    n8 = TreeNode(8)
    root = TreeNode('root',n7,n8)
 
    bt = BTree(root)
    print("preOrder".center(50,'-'))
    print(bt.preOrder(bt.root))
 
    print("inOrder".center(50,'-'))
    print (bt.inOrder(bt.root))
 
    print("postOrder".center(50,'-'))
    print (bt.postOrder(bt.root))
View Code
相关文章
相关标签/搜索