11 数据结构和排序算法

数据结构&算法

1.数据结构的存储一般常用的有几种?各有什么特点?

在计算机中,数据的存储结构可以采取如下四中方法来表现。

顺序存储方式

简单的说,顺序存储方式就是在一块连续的存储区域 一个接着一个的存放数据。顺序存储方式把逻辑上相连的结点存储在物理位置上相邻的存储单元里,结点间的逻辑关系由存储单元的邻接挂安息来体现。顺序存储方式也称为顺序存储结构(sequentialstorage structure),一般采用数组或者结构数组来描述。 线性存储方式主要用于线性逻辑结构的数据存放,而对于图和树等非线性逻辑结构则不适用。

链接存储方式

链接存储方式比较灵活,其不要求逻辑上相邻的结点在物理位置上相邻,结点间的逻辑关系由附加的引用字段表示。一个结点的引用字段往往指导下一个结点的存放位置。 链接存储方式也称为链接式存储结构(LinkedStorage Structure),一般在原数据项中增加应用类型来表示结点之间的位置关系。

索引存储方式

索引存储方式是采用附加索引表的方式来存储结点信息的一种存储方式。索引表由若干个索引项组成。索引存储方式中索引项的一般形式为:(关键字、地址)。其中,关键字是能够唯一标识一个结点的数据项。

索引存储方式还可以细分为如下两类:

稠密索引(Dense Index):这种方式中每个结点在索引表中都有一个索引项。其中,索引项的地址指示结点所在的的存储位置;

稀疏索引(Spare Index):这种方式中一组结点在索引表中只对应一个索引项。其中,索引项的地址指示一组结点的起始存储位置。

散列存储方式

散列存储方式是根据结点的关键字直接计算出该结点的存储地址的一种存储的方式。 在实际应用中,往往需要根据具体数据结构来决定采用哪一种存储方式。同一逻辑结构采用不同额存储方法,可以得到不同的存储结构。而且这四种节本存储方法,既可以单独使用,也可以组合起来对数据结构进行存储描述。

2.集合结构 线性结构 树形结构 图形结构 3.单向链表 双向链表 循环链表 4.数组和链表区别 5.堆、栈和队列

iOS常用算法和数据结构

数据结构初探这里面介绍了很多数据结构,大家还可以自行查阅更多资料

冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序、堆排序

常见的7种排序算法

七种常见的数组排序算法整理(C语言版本)

插入排序

时间复杂度为O(n^2)

它的基本操作是将一个记录插入到已排好序的有序表中,从而得到一个新的、记录数增1的有序表。举一个例子:{38,49,65,97,76,13,27}现在前四个数已经是排好序的了,对76进行插入排序,其实就是将76和前边四个值依次做比较,发现小于97 然后将97向后移将76插入。然后对13进行判断,13 小于38 插入到下标为0的位置,然后后边的再次向后移。时间复杂度为O(n^2)。

void InsertSort(int arr[], int length) {

    for (int i = 1; i < length; i++) {

        int j;

        if (arr[i] < arr[i - 1]) {

            int temp = arr[i];

            for (j = i - 1; j >= 0 && temp < arr[j]; j——) {

                arr[j + 1] = arr[j];

            }

            arr[j + 1] = temp;

        }

    }

}

希尔排序 

时间复杂度为O(n*d)

它其实是对直接插入排序的一种优化,它的基本思想是:先将整个待排序记录序列分割成若干个子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。因为对于直接插入排序来说,待排序的数列“基本有序”,或者 待排序的数量 N 比较小的时候,直接插入排序相对来说是比较快的时间复杂度最好的情况为O(n),那希尔排序就是从这两方面对直接排序进行的一种优化。

这里需要注意希尔排序的分割的一个特点:子序列的构成不是逐段分割而是将相隔某个“增量”的记录组成一个子序列。举个例子 {R1,R2,R3,R4,5,R6,R7,R8,R9,R10 },对这10个数进行排序。

先对{R1,R6},{R2,R7},{R3,R8},{R4,R9},{R5,R10},5个子序列进行直接插入排序。然后对{R1,R4,R7,R10},{R2,R5,R8},{R3,R6,R9},3个子序列进行直接插入排序。然后对整个序列进行直接插入排序。

但其实因为数学上存在的一些未解决的难题,用的并不多。主要是增量的取值。

快速排序和冒泡排序

二者其实都是借助交换这个概念进行排序的方法,这样不需额外开辟空间

冒泡排序的思想:先将R1和R2进行比较,如果R1>R2,那么交换R1和R2的位置,以此类推,直到比较到Rn-1 和Rn,这样第一次冒泡排序就结束了,目的是将最大的数放到了最后面,然后对剩下的n-1个数在进行这样的操作就把次大的数放在了n-1的位置。以此类推。这就好像是向海里扔了一个石头,比较小的像冒泡一样上浮,比较大的数像石头一样下沉。

时间复杂度为O(n^2)

void BubbleSort(int arr[], int length) {

    for (int i = 0; i < length; i++) {

        for (int j = 0; j < length -  i - 1; j++) {

            if (arr[j] > arr[j + 1]) {

                int temp;

                temp = arr[j + 1];

                arr[j + 1] = arr[j];

                arr[j] = temp;

            }

        }

    }

}

快速排序其实是冒泡排序的一种改进。它的基本思想是通过一趟排序将待排记录分割成两个独立的部分,其中一部分记录最小的值要大于另外一部分最大的值。,然后对这两个部分继续进行快速排序。

具体做法为:设置两个指针low和high分别指向待排序列的开始和结尾,记录下基准值baseval(待排序列的第一个记录),然后先从high所指的位置向前搜索直到找到一个小于baseval的记录并互相交换,接着从low所指向的位置向后搜索直到找到一个大于baseval的记录并互相交换,重复这两个步骤直到low=high为止。

平均时间复杂度为O(nlogn),最坏情况是O(n^2)

void QuickSort(int arr[],int left,int right)

{

    if (left >= right)

        return;

    int baseval = arr[left];

    int pLeft = left;

    int pRight = right;

    while (pLeft != pRight)

    {

        while (pLeft<pRight && arr[pRight] > baseval)

            pRight--;

        if (pLeft < pRight)

        {

            arr[pLeft] = arr[pRight];

            pLeft++;

        }

        while (pLeft<pRight && arr[pLeft] < baseval)

            pLeft++;

        if (pLeft < pRight)

        {

            arr[pRight] = arr[pLeft];

            pRight--;

        }

     }

     arr[pLeft] = baseval;

     QuickSort(arr,left,pLeft-1);

     QuickSort(arr,pLeft+1,right);

}

选择排序

时间复杂度为O(n^2)

具体来说,假设长度为n的数组arr,要按照从小到大排序,那么先从n个数字中找到最小值min1,如果最小值min1的位置不在数组的最左端(也就是min1不等于arr[0]),则将最小值min1和arr[0]交换,接着在剩下的n-1个数字中找到最小值min2,如果最小值min2不等于arr[1],则交换这两个数字,依次类推,直到数组arr有序排列。

void SelectionSort (int arr[],int length) {

    for(int i = 0;i < length;i++) {

        int index = i;

        for (int j = i+1;j<length;i++) {

            if (arr[j] < arr[index]) 

                index = j;

        }

        if(index == i)

            continue;

        else {

            int temp = arr[index];

            arr[index] = arr[i];

            arr[i] = temp;

        }

    }

}

堆排序 图解

时间复杂度为O(nlogn)   

堆的定义如下:n个元素的序列{k1,k2,….,kn}当且仅当满足以下条件时称之为堆

可以将堆看做是一个完全二叉树,并且每个节点的值都大于等于其左右孩子节点的值,称为大顶堆;或者小于等于其左右孩子节点的值,称为小顶堆。

堆排序就是利用堆进行排序的方法。基本思想为:

将待排序列构造成一个大(小)顶堆,整个序列的最大(小)值就是堆顶的根节点;

将根节点的值和堆数组的末尾元素交换,末尾原始就是最大(小)值,

然后将剩余的n-1个序列重新构造成一个堆,反复执行,最终得到一个有序序列。

void swap(int arr[],int a,int b) {

    int temp = arr[a];

    arr[a] = arr[b];

    arr[b] = temp;

}

//调整大顶堆

void adjustHeap(int arr[],int i,int length) {

    int temp = arr[i];

    for(int k = i*2 +1;k<length;k=k*2+1){ //从i节点的左子节点开始就是2i+1

        if(k+1<length && arr[k]<arr[k+1]) { //如果左子节点小于右子节点,k指向右子节点

            k++;

        }

        if(arr[k] > temp) { //如果子节点大于父节点,将子节点赋值给父节点(不用交换)

            arr[i] = arr[k];

            i = k;

        }else {

            break;

        }

    }

    arr[i] = temp;

}

void heapSort(int arr[],int length) {

    //1.构建大顶堆

    for(int i = length/2-1; i>0; i--){

        adjustHeap(arr,i,length);//从第一个非叶子节点从下至上,从右向左调整结构

    }

    //2.调整堆结构 交换堆顶元素与末尾元素

    for(int j=length-1;j>0;j--) {

        swap(arr,0,j);

        adjustHeap(arr,0,j);//重新对堆进行调整,因为除了0是因为交换后改变了,其他的排序没有改变,所以需要从堆顶的那个值调整

    }

}

归并排序

时间复杂度为O(nlogn)

归并的含义是将两个或两个以上的有序表组合成一个新的有序表。

假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列长度为1,然后两两归并,得到n/2个长度为2或者1的有序子序列,然后再两两归并。如此重复,直至得到一个长度为n的有序序列为止,这就是2-路归并排序。

//temp是一个与原数组等长度的临时数组。

void Sort(int arr[],int start ,int end,int *temp) {

    if(start >= end) return;

    int mid = (start + end)/2;

    Sort(arr,start,mid,temp);

    Sort(arr,mid+1,end,temp);

    Merge(arr,start,mid,end,temp);

}

void Merge (int arr[],int start,int mid,int end,int *temp) {

    int i = start;//左边序列最前边的指针

    int j = mid + 1;//右边序列的最前边的指针

    int t = 0; //临时数组指针

    while (i <= mid && j <= end) {

        if(arr[i] <= arr[j]) {

            //temp[t] = arr[i];

            //t++;i++;

            temp[t++] = arr[i++]; 上边两行等于这一行

        }else{

            temp[t++] = arr[j++];

        }

    }

    while (i<=mid)  {

        temp[t++] = arr[i++];

    }

    while (j<=end)  {

        temp[t++] = arr[j++];

    }

    t=0;

    while (start <= end) {

        arr[start++] = temp[t++];

    }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342