【数据结构】红黑树

         红黑树是一种二叉平衡树,在每一个结点增加了一个存储位表示结点的颜色,以维持它的平衡;


红黑树性质

(1)红黑树结点有如下域:color,key,left,right,p;我们把这些NIL结点是为指向外结点的指针,可以自己定义;

(2)每一个结点不是红的就是黑的,根节点和NIL结点都是黑的

(3)如果一个节点是红的,那么它的父亲和两个孩子都是黑的

(4)对于每一个结点,它到其子孙结点的所有路径上的包含相同数目的black结点,并不要求父节点到每一层的黑高度相同;

注1:可以使用同一的一个NIL来表示所有的叶子结点和根部的父节点,这样可以减少浪费内存,该NIL的父亲就是上述的普通结点,这样查找不成功时,就可以重新回到根节点root;

注2:设某个结点x出发到达任意一个节点的任意路径上,黑结点的个数称为该节点的黑高度,bh(x)表示;可归纳证明出根节点至少有2^h-1个结点,故n>=2^h-1,求得h<=2lg(n+1);


红黑树插入


(1)为了保持上述红黑树的性质(4),插入的节点z颜色一定是红色,其父节点为p;

(2)若刚开始树为空,则z是根节点,p为NIL节点,则将z的结点改为黑色;

(3)若p的颜色是黑色,则直接插入,因为红黑树的性质完全保持

(4)若p的颜色是红色,违反红黑树的性质3,此时要讨论情况;

(4.1)若p是p父亲的左孩子;

(4.1.1)若p的兄弟节点ps是红色,则改变p和ps为黑色,p父节点改为红色,此时p父节点为新的z,继续判别;

(4.1.2)若p的兄弟节点ps是黑色,且z是父亲的右孩子,做一次左旋,到情况(4.1.3);

(4.1.3)若p的兄弟节点ps是黑色,且z是父亲的左孩子,做一次右旋,改变z父亲颜色为黑色,z祖父颜色为红色,完毕;

(4.2)若p是p父亲的右孩子;情况与(4.1)类似,执行相反的动作;

(5)始终将根节点的颜色置为黑,因为处理时根节点有可能变红;

(6)时间复杂度为O(lgn),空间复杂度为O(1);


红黑树删除


(1)首先根据二叉平衡树的删除原理,找到被删除的节点y,x是y的子女(肯定只有一个),x会连接到y的父亲上;

(2)若y为红色,直接删掉,因为红黑树的性质保持不变;

(3)若y为黑色节点,x为红色,则直接删掉y,将x变为黑色;

(4)若y为黑色节点,x(可以为NIl节点)为黑色,则删掉y,将x变为双黑色

(4.1)若x是x父亲的左孩子,分别有四种情况来讨论;

(4.1.1)若x的兄弟是红色,则左旋一次,改变兄弟颜色为黑,父亲颜色为红,则它仍为新的x,继续;

(4.1.2)若x的兄弟是黑色,且x兄弟的两个孩子都是黑色,则去掉x的一重黑色,将兄弟颜色变为红,将去掉的一重黑色,给x的父亲,若x父亲是红色,则直接改成黑色,否则将变成双黑,成为新的x,继续;

(4.1.3)若x的兄弟是黑色,且x兄弟的右孩子黑色,左孩子是红色,则先将x兄弟那一块右旋,改色,使得x兄弟的右孩子变为红色;进入(4.1.4);

(4.1.4)若x的兄弟是黑色,且x兄弟的右孩子红色,左孩子可任意色,则左旋,改变x兄弟右孩子颜色为黑色,兄弟变为父亲的颜色,父亲变为黑色,自己去掉一重颜色,完毕;

(4.2)若x是x父亲的右孩子,分别也有四种情况来讨论,但与(4.1)对称,相反来处理;

(5)若根为双黑,直接变为单黑,返回;

(6)时间复杂度为O(lgn),空间复杂度为O(1);


代码分析

(本节代码取自nginx中红黑树相关的代码)

红黑树表示

typedef struct ngx_rbtree_node_s  ngx_rbtree_node_t;

//红黑树节点的定义
struct ngx_rbtree_node_s {
    ngx_rbtree_key_t       key;   //关键字
    ngx_rbtree_node_t     *left;  //左子节点
    ngx_rbtree_node_t     *right; //右子节点
    ngx_rbtree_node_t     *parent;//父节点
    u_char                 color; //颜色,非红即黑
    u_char                 data;  //1个字节的节点数据,空间较小很少使用;
};

typedef struct ngx_rbtree_s  ngx_rbtree_t;

//为解决不同节点含有相同关键字元素冲突问题,设置该指针,灵活的添加冲突元素
typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root,ngx_rbtree_node_t *node,ngx_rbtree_node_t *sentinel);

//红黑树的定义
struct ngx_rbtree_s {
    ngx_rbtree_node_t     *root;      //指向树的根节点,根结点也是数据元素
    ngx_rbtree_node_t     *sentinel;  //指向哨兵NIL节点
    ngx_rbtree_insert_pt   insert;    //添加元素的函数指针
};


节点颜色相关宏定义

//颜色设置
#define ngx_rbt_red(node)               ((node)->color = 1)
#define ngx_rbt_black(node)             ((node)->color = 0)
#define ngx_rbt_is_red(node)            ((node)->color)
#define ngx_rbt_is_black(node)          (!ngx_rbt_is_red(node))
#define ngx_rbt_copy_color(n1,n2)      (n1->color = n2->color)


寻找值最小的节点

//内联函数,寻找值最小的节点
static ngx_inline ngx_rbtree_node_t *
ngx_rbtree_min(ngx_rbtree_node_t *node,ngx_rbtree_node_t *sentinel)
{
    while (node->left != sentinel) {
        node = node->left;
    }

    return node;
}


红黑树初始化

#define ngx_rbtree_init(tree,s,i)                                           \
    ngx_rbtree_sentinel_init(s);                                              \
    (tree)->root = s;                                                         \
    (tree)->sentinel = s;                                                     \
    (tree)->insert = i

其中tree代表红黑树管理结构,s代表NIL节点,i代表添加元素的函数;


/* a sentinel must be black */

#define ngx_rbtree_sentinel_init(node)  ngx_rbt_black(node)

左旋转和右旋转

//左旋转
static ngx_inline void
ngx_rbtree_left_rotate(ngx_rbtree_node_t **root,ngx_rbtree_node_t *sentinel,ngx_rbtree_node_t *node)
{
    ngx_rbtree_node_t  *temp;

    temp = node->right;     //要旋转节点的右孩子p
    node->right = temp->left; //当前节点的右孩子指向改变p的左孩子

    if (temp->left != sentinel) {  //不是NIL节点时
        temp->left->parent = node; //修改父亲的指向
    }

    temp->parent = node->parent;  //p指向要旋转节点的父亲

    if (node == *root) {   //如果当前旋转节点的是根结点,那么根指向要改变
        *root = temp;

    } else if (node == node->parent->left) {  //左孩子时
        node->parent->left = temp;  //新的左孩子

    } else {
        node->parent->right = temp; //新的右孩子
    }

    temp->left = node;   //p的左孩子指向
    node->parent = temp; //父亲改变
}

//右旋转,与上述左边旋转对称
static ngx_inline void
ngx_rbtree_right_rotate(ngx_rbtree_node_t **root,ngx_rbtree_node_t *node)
{
    ngx_rbtree_node_t  *temp;

    temp = node->left;
    node->left = temp->right;

    if (temp->right != sentinel) {
        temp->right->parent = node;
    }

    temp->parent = node->parent;

    if (node == *root) {
        *root = temp;

    } else if (node == node->parent->right) {
        node->parent->right = temp;

    } else {
        node->parent->left = temp;
    }

    temp->right = node;
    node->parent = temp;
}


红黑树节点插入

void
ngx_rbtree_insert_value(ngx_rbtree_node_t *temp,ngx_rbtree_node_t *sentinel)
{
    //temp为初始时为指向根节点
    
    ngx_rbtree_node_t  **p;

    for ( ;; ) {

        p = (node->key < temp->key) ? &temp->left : &temp->right;  //目标节点的值小于当前节点的值时,向左走即走向左节点,否则向右

        if (*p == sentinel) {   //一直找到该节点的孩子指向的是NIL节点,直接break
            break;
        }

        temp = *p;      //改变方向
    }

    //注意使用地址的地址,才能改变某节点的孩子的指向,否则改变不了
    *p = node;        //该处指向新的节点,*p(某节点的孩子)本来指向sentinel,现在指向node了
    node->parent = temp;
    node->left = sentinel;  //指向NIL
    node->right = sentinel;
    ngx_rbt_red(node);  //插入的节点均为红色
}

void
ngx_rbtree_insert(ngx_rbtree_t *tree,ngx_rbtree_node_t *node)
{
    ngx_rbtree_node_t  **root,*temp,*sentinel;

    /* a binary tree insert */

    root = (ngx_rbtree_node_t **) &tree->root;  //指向根节点指针的指针,可改变指向根节点指针的指向
    sentinel = tree->sentinel;        //取出NIL节点

    if (*root == sentinel) {         //说明此时红黑树为空
        node->parent = NULL;
        node->left = sentinel;      //指向NIL
        node->right = sentinel;
        ngx_rbt_black(node);        //node节点直接置为黑
        *root = node;               //根节点直接指向该节点

        return;
    }

    tree->insert(*root,node,sentinel);   
    //插入节点,如ngx_rbtree_insert_value(ngx_rbtree_node_t *temp,ngx_rbtree_node_t *sentinel)
    
    /* re-balance tree */

    //红黑树平衡
    
    //当前节点不为根节点,而且当前节点的父亲为红色时
    while (node != *root && ngx_rbt_is_red(node->parent)) {

        if (node->parent == node->parent->parent->left) {   //当前节点的父亲是左节点时
            temp = node->parent->parent->right;    //当前节点的父亲的右兄弟

            //向上转移
            if (ngx_rbt_is_red(temp)) {  
                //对应算法导论第三版P179的情况1
                         
                ngx_rbt_black(node->parent);      //当前节点的父亲变黑
                ngx_rbt_black(temp);              //当前节点的父亲的右兄弟变黑
                ngx_rbt_red(node->parent->parent);//当前节点的父亲的父亲变红
                node = node->parent->parent;      //当前节点变为该当前节点的父亲的父亲

            } else {  //当前节点的父亲的右兄弟为黑色时
            
                //对应算法导论第三版P179的情况2
                
                //当前节点是右节点时,先左旋转
                if (node == node->parent->right) {
                    node = node->parent;   //当节点变为父节点
                    ngx_rbtree_left_rotate(root,sentinel,node);  //当前节点左旋转
                }

                //对应算法导论第三版P179的情况3
                ngx_rbt_black(node->parent);  //当前节点的父亲变为黑色
                ngx_rbt_red(node->parent->parent); //当前节点的父亲的父亲变为黑色
                ngx_rbtree_right_rotate(root,node->parent->parent);  //当前节点父亲的父亲右旋转
            }

        } else {   //相反,对称操作
            temp = node->parent->parent->left;

            if (ngx_rbt_is_red(temp)) {
                ngx_rbt_black(node->parent);
                ngx_rbt_black(temp);
                ngx_rbt_red(node->parent->parent);
                node = node->parent->parent;

            } else {
                if (node == node->parent->left) {
                    node = node->parent;
                    ngx_rbtree_right_rotate(root,node);
                }

                ngx_rbt_black(node->parent);
                ngx_rbt_red(node->parent->parent);
                ngx_rbtree_left_rotate(root,node->parent->parent);
            }
        }
    }

    ngx_rbt_black(*root);  //最后始终着色根结点为黑色
}


红黑树节点删除

void
ngx_rbtree_delete(ngx_rbtree_t *tree,ngx_rbtree_node_t *node)
{
    ngx_uint_t           red;
    ngx_rbtree_node_t  **root,*sentinel,*subst,*w;

    /* a binary tree delete */

    root = (ngx_rbtree_node_t **) &tree->root;
    sentinel = tree->sentinel;

    //subst为要替代的节点
    //temp为将要替代节点的孩子
    if (node->left == sentinel) {   //删除节点为左孩子为NIL节点
        temp = node->right;   
        subst = node;

    } else if (node->right == sentinel) {
        temp = node->left;
        subst = node;

    } else {
        subst = ngx_rbtree_min(node->right,sentinel);  //找到以删除节点右孩子为根的最小值节点

        if (subst->left != sentinel) {  
            temp = subst->left;
        } else {                        //通过ngx_rbtree_min找到的,该最小值节点的左孩子一定是NIL
            temp = subst->right;
        }
    }

    if (subst == *root) {  //subst为要删除的节点为根节点时
        *root = temp;     //指向新的根节点
        ngx_rbt_black(temp);

        /* DEBUG stuff */
        node->left = NULL;
        node->right = NULL;
        node->parent = NULL;
        node->key = 0;

        return;
    }

    red = ngx_rbt_is_red(subst);    //直接取颜色

    if (subst == subst->parent->left) {   
        subst->parent->left = temp;   //父亲的左孩子指向要删除节点的左孩子

    } else {
        subst->parent->right = temp;  //父亲的右孩子指向要删除节点的右孩子
    }

    if (subst == node) {   //要删除的节点为原始的节点时

        temp->parent = subst->parent;  //父亲节点指向的改变

    } else {

        if (subst->parent == node) {       //通过ngx_rbtree_min一开始就不成立,即node->right的left为NIL节点
            temp->parent = subst;        

        } else {
            temp->parent = subst->parent;    //通过ngx_rbtree_min找到的
        }

        subst->left = node->left;   //指向node的左孩子
        subst->right = node->right; //指向node的右孩子
        subst->parent = node->parent;
        ngx_rbt_copy_color(subst,node);  //将node节点的颜色复制给subst节点

        if (node == *root) {
            *root = subst;    //指向新的根节点

        } else {
            if (node == node->parent->left) {
                node->parent->left = subst;  //改变父亲孩子指向到subst
            } else {
                node->parent->right = subst;
            }
        }

        if (subst->left != sentinel) {    //改变subst孩子的指向
            subst->left->parent = subst;
        }

        if (subst->right != sentinel) {
            subst->right->parent = subst;
        }
    }

    /* DEBUG stuff */
    node->left = NULL;
    node->right = NULL;
    node->parent = NULL;
    node->key = 0;

    if (red) {    //如果删除的节点的颜色为红色,直接返回
        return;
    }

    //平衡
    /* a delete fixup */

    //孩子节点,如果孩子节点为黑色时
    while (temp != *root && ngx_rbt_is_black(temp)) {

        if (temp == temp->parent->left) {   //temp为左孩子时
            w = temp->parent->right;    //temp的右兄弟

            //算法导论第三版P186中的情况a
            if (ngx_rbt_is_red(w)) {  //temp的右兄弟为红色时
                ngx_rbt_black(w);     //temp的右兄弟变为黑色
                ngx_rbt_red(temp->parent); //temp的父亲变为红色色
                ngx_rbtree_left_rotate(root,temp->parent);   //以temp->parent左旋转
                w = temp->parent->right;    //新的右兄弟,将会到以下几种情况
            }

            //算法导论第三版P186中的情况b
            //当temp的右兄弟的两个孩子均为黑色时
            if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {
                ngx_rbt_red(w); //将右兄弟变为红色
                temp = temp->parent;    //将以新的temp进行新的一次循环

            } else {
                //算法导论第三版P186中的情况c
                if (ngx_rbt_is_black(w->right)) {   //如果右兄弟的右孩子是黑色的
                    ngx_rbt_black(w->left);   //w的左孩子变黑
                    ngx_rbt_red(w);           //w变红
                    ngx_rbtree_right_rotate(root,w);   //以w右旋
                    w = temp->parent->right;  //新的右兄弟,直接执行情况c
                }

                //算法导论第三版P186中的情况d
                ngx_rbt_copy_color(w,temp->parent);  //右兄弟变成父亲的颜色
                ngx_rbt_black(temp->parent);  //temp的父亲变黑
                ngx_rbt_black(w->right);      //右兄弟的右孩子变黑
                ngx_rbtree_left_rotate(root,temp->parent);  //以temp的父亲右旋
                temp = *root;  //结束,直接指向根结点
            }

        } else {  //与上述情况相反,做对称操作
            w = temp->parent->left;

            if (ngx_rbt_is_red(w)) {
                ngx_rbt_black(w);
                ngx_rbt_red(temp->parent);
                ngx_rbtree_right_rotate(root,temp->parent);
                w = temp->parent->left;
            }

            if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {
                ngx_rbt_red(w);
                temp = temp->parent;

            } else {
                if (ngx_rbt_is_black(w->left)) {
                    ngx_rbt_black(w->right);
                    ngx_rbt_red(w);
                    ngx_rbtree_left_rotate(root,w);
                    w = temp->parent->left;
                }

                ngx_rbt_copy_color(w,temp->parent);
                ngx_rbt_black(temp->parent);
                ngx_rbt_black(w->left);
                ngx_rbtree_right_rotate(root,temp->parent);
                temp = *root;
            }
        }
    }

    //temp为红色时,直接将temp置为黑色
    ngx_rbt_black(temp);
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


【啊哈!算法】算法3:最常用的排序——快速排序       上一节的冒泡排序可以说是我们学习第一个真正的排序算法,并且解决了桶排序浪费空间的问题,但在算法的执行效率上却牺牲了很多,它的时间复杂度达到了O(N2)。假如我们的计算机每秒钟可以运行10亿次,那么对1亿个数进行排序,桶排序则只需要0.1秒,而冒泡排序则需要1千万秒,达到115天之久,是不是很吓人。那有没有既不浪费空间又可以快一点的排序算法
匿名组 这里可能用到几个不同的分组构造。通过括号内围绕的正则表达式就可以组成第一个构造。正如稍后要介绍的一样,既然也可以命名组,大家就有考虑把这个构造作为匿名组。作为一个实例,请看看下列字符串: “08/14/57 46 02/25/59 45 06/05/85 18 03/12/88 16 09/09/90 13“ 这个字符串就是由生日和年龄组成的。如果需要匹配年两而不要生日,就可以把正则
选择排序:从数组的起始位置处开始,把第一个元素与数组中其他元素进行比较。然后,将最小的元素方式在第0个位置上,接着再从第1个位置开始再次进行排序操作。这种操作一直到除最后一个元素外的每一个元素都作为新循环的起始点操作过后才终止。 public void SelectionSort() { int min, temp;
public struct Pqitem { public int priority; public string name; } class CQueue { private ArrayList pqueue; public CQueue() { pqueue
在编写正则表达式的时候,经常会向要向正则表达式添加数量型数据,诸如”精确匹配两次”或者”匹配一次或多次”。利用数量词就可以把这些数据添加到正则表达式里面了。 数量词(+):这个数量词说明正则表达式应该匹配一个或多个紧紧接其前的字符。 string[] words = new string[] { "bad", "boy", "baad", "baaad" ,"bear", "b
来自:http://blog.csdn.net/morewindows/article/details/6678165/归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列
插入排序算法有两层循环。外层循环会啄个遍历数组元素,而内存循环则会把外层循环所选择的元素与该元素在数组内的下一个元素进行比较。如果外层循环选择的元素小于内存循环选择的元素,那么瘦元素都想右移动以便为内存循环元素留出位置。 public void InsertionSort() { int inner, temp;
public int binSearch(int value) { int upperBround, lowerBound, mid; upperBround = arr.Length - 1; lowerBound = 0; while (lowerBound <= upper
虽然从表内第一个节点到最后一个节点的遍历操作是非常简单的,但是反向遍历链表却不是一件容易的事情。如果为Node类添加一个字段来存储指向前一个节点的连接,那么久会使得这个反向操作过程变得容易许多。当向链表插入节点的时候,为了吧数据复制给新的字段会需要执行更多的操作,但是当腰吧节点从表移除的时候就能看到他的改进效果了。 首先需要修改Node类来为累增加一个额外的链接。为了区别两个连接,这个把指
八、树(Tree)树,顾名思义,长得像一棵树,不过通常我们画成一棵倒过来的树,根在上,叶在下。不说那么多了,图一看就懂:当然了,引入了树之后,就不得不引入树的一些概念,这些概念我照样尽量用图,谁会记那么多文字?树这种结构还可以表示成下面这种方式,可见树用来描述包含关系是很不错的,但这种包含关系不得出现交叉重叠区域,否则就不能用树描述了,看图:面试的时候我们经常被考到的是一种叫“二叉树”的结构,二叉
Queue的实现: 就像Stack类的实现所做的一样,Queue类的实现用ArrayList简直是毋庸置疑的。对于这些数据结构类型而言,由于他们都是动态内置的结构,所以ArrayList是极好的实现选择。当需要往队列中插入数据项时,ArrayList会在表中把每一个保留的数据项向前移动一个元素。 class CQueue { private ArrayLis
来自:http://yingyingol.iteye.com/blog/13348911 快速排序介绍:快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地
Stack的实现必须采用一种基本结构来保存数据。因为再新数据项进栈的时候不需要担心调整表的大小,所以选择用arrayList.using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Collecti
数组类测试环境与排序算法using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace Data_structure_and_algorithm{ class CArray { pr
一、构造二叉树 二叉树查找树由节点组成,所以需要有个Node类,这个类类似于链表实现中用到的Node类。首先一起来看看Node类的代码。 public class Node { public int Data; public Node Left; public Node Right; public v
二叉树是一种特殊的树。二叉树的特点是每个结点最多有两个儿子,左边的叫做左儿子,右边的叫做右儿子,或者说每个结点最多有两棵子树。更加严格的递归定义是:二叉树要么为空,要么由根结点、左子树和右子树组成,而左子树和右子树分别是一棵二叉树。 下面这棵树就是一棵二叉树。         二叉树的使用范围最广,一棵多叉树也可以转化为二叉树,因此我们将着重讲解二叉树。二叉树中还有连两种特殊的二叉树叫做满二叉树和
上一节中我们学习了队列,它是一种先进先出的数据结构。还有一种是后进先出的数据结构它叫做栈。栈限定只能在一端进行插入和删除操作。比如说有一个小桶,小桶的直径只能放一个小球,我们现在向小桶内依次放入2号、1号、3号小球。假如你现在需要拿出2号小球,那就必须先将3号小球拿出,再拿出1号小球,最后才能将2号小球拿出来。在刚才取小球的过程中,我们最先放进去的小球最后才能拿出来,而最后放进去的小球却可以最先拿
msdn中的描述如下:(?= 子表达式)(零宽度正预测先行断言。) 仅当子表达式在此位置的右侧匹配时才继续匹配。例如,w+(?=d) 与后跟数字的单词匹配,而不与该数字匹配。此构造不会回溯。(?(零宽度正回顾后发断言。) 仅当子表达式在此位置的左侧匹配时才继续匹配。例如,(?此构造不会回溯。msdn描述的比较清楚,如:w+(?=ing) 可以匹配以ing结尾的单词(匹配结果不包括ing),(
1.引入线索二叉树 二叉树的遍历实质上是对一个非线性结构实现线性化的过程,使每一个节点(除第一个和最后一个外)在这些线性序列中有且仅有一个直接前驱和直接后继。但在二叉链表存储结构中,只能找到一个节点的左、右孩子信息,而不能直接得到节点在任一遍历序列中的前驱和后继信息。这些信息只有在遍历的动态过程中才能得到,因此,引入线索二叉树来保存这些从动态过程中得到的信息。 2.建立线索二叉树 为了保
排序与我们日常生活中息息相关,比如,我们要从电话簿中找到某个联系人首先会按照姓氏排序、买火车票会按照出发时间或者时长排序、买东西会按照销量或者好评度排序、查找文件会按照修改时间排序等等。在计算机程序设计中,排序和查找也是最基本的算法,很多其他的算法都是以排序算法为基础,在一般的数据处理或分析中,通常第一步就是进行排序,比如说二分查找,首先要对数据进行排序。在Donald Knuth 的计算机程序设