数据结构-查找与排序

查找

  • 对查找表的操作有
    • 静态查找表
      • 查询某个“特定的”数据元素是否在查找表中
      • 检索某个“特定的”数据元素的各种属性
    • 动态查找表
      • 在查找表中插入一个数据元素
      • 在查找表中删去某个数据元素

静态查找表

  • 顺序表的查找
    • 查找过程: 从表中最后一个记录开始,逐个进行记录的关键字和给定值的比较,若某个记录的关键字和给定值比较相等,则查找成功,找到所查记录;反之,若直至第一个记录,其关键字和给定值比较都不等,则表明表中没有所查记录,查找不成功。
  • 有序表的查找
    • 使用折半查找实现
    • 先确定待查记录所在的范围(区间),然后逐步缩小范围直到找到或找不到该记录为止。
    • 时间复杂度:log2n
     public static int biSearch(int [] array, int a){
     	int first = 0;
     	int end = array.length -1;
     	int mid;
     	while(first <= end){
    		mid = (first + end)/2; //中间位置
    		if(array[mid] == a){
    			return mid + 1;
    		}else if(array[mid] < a){
    			first = mid + 1;
    		}else{
    			end = mid - 1;
    		}
    	}
    	return -1;
     }
    
  • 静态树表的查找
    • 有序表中各记录的查找概率不等
  • 索引顺序表的查找
    • 使用分块查找实现
    • 在顺序查找中,建立一个“索引表”
    • 查找过程:先确定待查记录所在的块,然后在块中顺序查找

动态查找表

  • 二叉排序树(二叉查找树)
    • 定义:或者是一颗空树;或者是具有下列性质的二叉树:(1)左子树不空,则左子树上所有结点的值均小于它的根结点的值;(2)右子树不空,则右子树上所有结点的值均大于它的根节点的值;(3)它的左右子树分别为二叉排序树。
      在这里插入图片描述
  • 平衡二叉树(AVL树)
    • 定义:是一颗空树,或者是具有下列性质的二叉树:它的左右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1
      在这里插入图片描述
    • 平衡方法
      • 单向右旋平衡
      • 单向左旋平衡
      • 双向旋转(先左后右)平衡
      • 双向旋转(先右后左)平衡
  • B-树
    • 定义:是一种平衡的多路查找树,在文件系统中很有用
    • m阶B-数的结构:或是空树,或为满足下列特征的m叉树:(1)树中每个接地那至多有m颗子树;(2)若根结点不是叶子结点,则至少有两棵树;(3)除根之外的所有非终端结点至少有[m/2]颗子树;(4)所有的非终端结点中包含下列信息数据(n,A0,K1,A1,K2,A2,…,Kn,An,),其中Ki为关键字;(5)所有的叶子结点都出现在同一层次上,并且不带信息。
      在这里插入图片描述
    • B-树的查找过程是一个顺指针查找结点和在结点的关键字中进行查找交叉进行的过程
    • B-树主要用作文件索引
  • B+树
    • B+树是应文件系统所需而出的一种B-树的变型树。
    • 一颗m阶的B+树和m阶的B-树的差异在于:(1)有n棵子树的结点中含有n个关键字;(2)所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接;(3)所有的非终端结点可以看成是索引部分,结点中仅含有其子树(根结点)中的最大(或最小)关键字
      在这里插入图片描述
  • 键树(数字查找树)
    • 定义:一颗度>=2的树,树中的每个结点中不是包含一个或几个关键字,而是只含有组成关键字的符号。
      在这里插入图片描述

哈希表

  • 哈希表
    • 定义
      • 在查找时,根据对应关系f找到给定值K得像f(K),若结构中存在关键字和K相等的记录,则必定在f(K)的存储位置上。
    • 哈希函数是一个映射
    • 冲突:对不同关键字可能得到同一哈希地址。
  • 构造哈希函数的方法
    • 直接定址法:取关键字或关键字的某个线性函数值为哈希地址。即H(key) = key 或 H(key)= a · key + b.
    • 数字分析法
    • 平方取中法
    • 折叠法
    • 除留余数法:取挂念自被某个不大于哈希表表长m的数p除后所得的余数为哈希地址,即H(key)= key MOD p,p<=m.
  • 处理冲突的方法
    • 开放定址法 Hi=(H(key) + di) MOD m
    • 再哈希法 Hi=RHi(key)
    • 链地址法:将所有关键字为同义词的记录存储在同一线性链表中。
    • 建立一个公共溢出区

排序

内部排序

  • 定义
    • 待排序记录存放在计算机随机存储器中进行的排序过程

插入排序

  • 直接插入排序

    • 基本操作:将一个记录插入到已排好序的有序表中,从而得到一个新的、记录数增1的有序表。
    • 时间复杂度O(n^2^)
    • 算法:
    void InsertSort(int a[] ){
     	for( i=1;i<a.length;i++) {
             int temp=a[i];
             for(  j=i-1;j>=0&&temp<a[j];j--) 
                    a[j+1]=a[j]; 
             a[j+1]=temp;
         }
    }
    
     ![在这里插入图片描述](https://img-blog.csdnimg.cn/2019101714211247.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x5bTNsYXZlbmRlcg==,size_16,color_FFFFFF,t_70)
    
  • 折半插入排序

    • 基本操作:在一个有序表中进行查找和插入,“查找”操作使用“折半查找”实现
    • 时间复杂度:O(n^2^)
    • 算法
    void BInsertSort(int a[] ){
     	for( i=1;i<a.length;i++) {
             int temp=a[i];
             int low = 1; int high = i -1;
             while(low <= heigh){
             	int m = (low + high)/2;
             	if(a[i] < a[m])  high = m - 1;
             	else low = m + 1;
             }
             for(  j=i-1;j > hight;j--) 
                    a[j+1]=a[j]; 
             a[j+1]=temp;
         }
    }
    
  • 2-路插入排序

    • 操作思想:在折半插入排序的基础上,减少排序过程中移动记录的次数,但需要n个记录的辅助空间。
    • 基本操作:另外设置一个同存储记录的数组大小相同的数组 d,将无序表中第一个记录添加进 d[0] 的位置上,然后从无序表中第二个记录开始,同 d[0] 作比较:如果该值比 d[0] 大,则添加到其右侧;反之添加到其左侧。
    • 2-路插入排序中,移动记录的次数约为n2/8。
      在这里插入图片描述
    • 算法
    void 2BInsertSort(int a[] ){
     	int i,first,final,k;
        first = final = 0;//分别记录temp数组中最大值和最小值的位置
        temp[0] = arr[0];
        for (i = 1; i < n; i ++){
            // 待插入元素比最小的元素小
            if (arr[i] < temp[first]){
                first = (first - 1 + n) % n;
                temp[first] = arr[i];
            }
            // 待插入元素比最大元素大
            else if (arr[i] > temp[final]){
                final = (final + 1 + n) % n;
                temp[final] = arr[i];
            }
            // 插入元素比最小大,比最大小
            else {
                k = (final + 1 + n) % n;
                //当插入值比当前值小时,需要移动当前值的位置
                while (temp[((k - 1) + n) % n] > arr[i]) {
                    temp[(k + n) % n] =temp[(k - 1 + n) % n];
                    k = (k - 1 + n) % n;
                }
                //插入该值
                temp[(k + n) % n] = arr[i];
                //因为最大值的位置改变,所以需要实时更新final的位置
                final = (final + 1 + n) % n;
            }
        }
        // 将排序记录复制到原来的顺序表里
        for (k = 0; k < n; k ++) {
            arr[k] = temp[(first + k) % n];
        }
    }
    
    
  • 表插入排序

    • 排序过程:首先将静态链表中数据下标为“1”的分量(结点)和表头结点构成一个循环链表,然后依次将下标为“2”至“n”的分量(结点)按记录关键字非递减有序插入到循环链表中。
    • 时间复杂度:O(n^2^).
  • 希尔排序

    • 基本思想:先将整个待排记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
    • 特点:子序列的构成不是简单的“逐段分割”,而是将相隔某个“增量”的记录组成一个子序列。
    • 时间复杂度:O(n^3/2^)
    public void shellSort(int[] a){
    	int dk = a.length /2;
    	while(dk >= 1){
    		ShellInsertSort(a, dk);
    		dk = dk/2;
    	}
    }
    public void ShellInsertSort(int[] a, int dk){
    	for( i=dk;i<a.length;i++) {
    		if(a[i] < a[i-dk]){
    			int i,j;
    			int temp = a[i];
    			a[i] = a[i-dk];
    			for(j = i-dk; j >0 && temp < a[j]; j = j -dk){
    				a[j + dk ] = a[j];
    			}
    			a[j + dk ] = temp;
    		}
         }
    }
    

快速排序

  • 冒泡排序
    • 基本操作:首先将第一个记录的关键字和第二个记录的关键字进行比较,若为逆序,则将两个记录交换,然后比较第二个记录和第三个记录的关键字,依次类推,直至第n-1个记录和第n个记录的关键字进行比较为止。
    • 时间复杂度:O(n^2^)
    public static void bubbleSort(int []a, int n){
    	int i,j;
    	for(i = 0; i< n ;i++){
    		for(j = 1; j < n-i; j++){
    			if(a[j-1] > a[j]){
    				int temp;
    				temp = a[j-1];
    				a[j-1] = a[j];
    				a[j] = temp;
    			}
    		}
    	}
    }
    
    在这里插入图片描述
  • 快速排序
    • 基本思想:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对着两部分记录继续进行排序,已达到整个序列有序。
    • 支点(枢轴,pivot):通常选第一个记录作为支点。在排序过程中,只有在一趟排序结束时,low = high的位置才是枢轴记录的最后位置。
    • 时间复杂度:O(nlogn)
    public void sort(int[] a, int low, int high){
    	int start = low;
    	int end = high;
    	int key = a[low];
    	while(end < start){
    		while(end > start && a[end] >= key)
    			end --;
    		if(a[end] < = key){
    			int temp = a[end];
    			a[end] = a[start];
    			a[start] = temp;
    		}
    		while(end > start && a[start] <= key)
    			start ++;
    		if(a[start] > = key){
    			int temp = a[start];
    			a[start] = a[end];
    			a[end] = temp;
    		}
    		if(start > low) sort(a, low, start -1);
    		if(end < high) sort(a, end+1, high);
    	}
    }
    

在这里插入图片描述

选择排序

  • 基本思想:每一趟在n-i+1(i=1,2,…,n-1)个记录中选取关键字最小的记录作为有序序列中第i个记录。

  • 简单选择排序

    • 操作:通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i个记录交换。
    • 时间复杂度:O(n2)
  • 树形选择排序

    • 思想:首先对n个记录的关键字进行两两比较,然后再其中[n/2]个较小者之间再进行两两比较,如此充分,直至选出最小关键字的记录为止。
    • 时间复杂度:O(nlogn)
  • 堆排序

    • 堆排序只需要一个记录大小的辅助空间,每个待排序的记录仅占有一个存储空间。
    • 堆的定义:n个元素的序列{k1,k2,…,kn}当且仅当满足下关系,
      • ki <= k2i, ki<=k2i+1
      • 或ki >= k2i, ki>=k2i+1
    • 时间复杂度:O(nlogn)
    • 大顶堆

归并排序

  • 思路:将两个或两个以上的有序表组合成一个新的有序表
  • 时间复杂度:O(nlogn)
  • 稳定的排序方法
public static void mergeSort(int[] data) {
   sort(data, 0, data.length - 1);
}
public static void sort(int[] data, int left, int right) {
   if (left >= right)
   return;
   // 找出中间索引
   int center = (left + right) / 2;
   // 对左边数组进行递归
   sort(data, left, center);
   // 对右边数组进行递归
   sort(data, center + 1, right);
   // 合并
   merge(data, left, center, right);
}
public static void merge(int[] data, int left, int center, int right) {
   // 临时数组
   int[] tmpArr = new int[data.length];
   // 右数组第一个元素索引
   int mid = center + 1;
   // third 记录临时数组的索引
   int third = left;
   // 缓存左数组第一个元素的索引
   int tmp = left;
   while (left <= center && mid <= right) {
   // 从两个数组中取出最小的放入临时数组
   	if (data[left] <= data[mid]) {
   		tmpArr[third++] = data[left++];
   	} else {
   		tmpArr[third++] = data[mid++];
   	}
   }
   // 剩余部分依次放入临时数组(实际上两个while只会执行其中一个)
   while (mid <= right) {
   	tmpArr[third++] = data[mid++];
   }
   while (left <= center) {
   	tmpArr[third++] = data[left++];
   }
   // 将临时数组中的内容拷贝回原数组中
   // (原left-right范围的内容被复制回原数组)
   while (tmp <= right) {
   	data[tmp] = tmpArr[tmp++];
   }
}

在这里插入图片描述

基数排序

  • 借助多关键字排序思想对单逻辑关键字进行排序
  • 多关键字的排序
  • 链式基数排序

内部排序的比较

在这里插入图片描述

外部排序

  • 定义
    • 待排序记录的数量很大,以致内存一次不能容纳全部记录,在排序过程中尚需读外存进行访问的排序过程。
  • 外存信息的存取
    • 磁带信息的存取
    • 磁盘信息的存取
  • 外部排序的方法
    • 归并段(顺串):按可用内存大小,将外存上含n个记录的文件分成若干长度为l的子文件或段(segment),一次读入内存并利用有效的内部排序方法对它们进行排序,并将排序后得到的有序子文件重新写入外存。
    • 对归并段进行逐趟归并,使归并段逐渐由小至大,直至得到整个有序文件为止。
  • 多路平衡归并的实现
  • 置换-选择排序
  • 最佳归并树
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值