数据结构笔记二

数据结构笔记二



前言

内容从快排开始,到哈希表和有序表为止,最后整理排序的时间复杂度和空间复杂度,并比较各个排序算法的优劣度


1.快排

快排据老师介绍有不同的版本,这里写的就是最后的版本,老师的代码写的很精妙,要copy的话就要完全按照老师的来,我整了半天决定还是自己写,原理是选择一个随机的数,然后将整个数组分为大于这个数的数在右边,小于这个数的数在左边,中间的是等于这个数的数,然后对左右两边范围的数重复这一个过程,简单来描述就是,每次随机选中一个数将这个数放到整个数组从小到大排序他应该在的位置,然后是下一个随机数,这样不断的循环整个数组也就排好序了。

 public  static  void QuickSort(int[]arr,int L,int R){
        if(L<R) {
            int ran=arr[L+(int)(Math.random() * (R - L))];
            int[] p = partition(arr, L, R,ran);//也就是快排的排序
            QuickSort(arr, L, p[0]);
            QuickSort(arr, p[1], R);
        }
    }
    public static  int[] partition(int[]arr,int L,int R,int ran){
        int left=L-1;//<的左边界
        int right=R+1;//>的右边界,选中的随机数固定在R处,在循环结束时,在与>右边界交换
       //L检查整个数组的指针
        while(L<right){
            if (arr[L] < ran) {
                left++;
                if(L!=left)swap(arr,left,L);//防止自己和自己交换
                L++;
              //简单的写法为 swap(arr,++left,L++);但是不太直观
            }
            else if(arr[L]>ran){//这里边界移动了包含了一个未检查的数,所以L的位置不能变动
                right--;
                if(L!=right)swap(arr,right,L);
                //这里如上也有简便写法
            }
            else L++;
        }
        return new int[]{left,right};
    }

2.堆排序

原理就是借助大根堆或小根堆的性质,每次将最小或者最大的数放到数组中应该在 的位置,比如说把最大的数放到最后,最小的数放到最前面。然后将堆的最后一个数放到堆顶再进行一次堆化,具体的原理可以另行百度,这里简单解释

public static  void HeapSort(int[]arr){
        if(arr==null||arr.length<2) return;
        HeapInsert(arr);//先将数组堆排序
        int heapsize=arr.length;
        for(int i=heapsize-1;i>0;i--){
            swap(arr,i,0);
            heapsize--;
            Heapify(arr,heapsize,0);
        }
    }
    public static void HeapInsert(int[]arr){//将数组变成大根堆
        int i=1;
        for(int heapsize=2;heapsize<=arr.length;heapsize++){
            i=heapsize-1;
            while (arr[i]>arr[(i-1)/2]) {//与父节点作比较
                swap(arr,i,(i-1)/2);
                i=(i-1)/2;
                if(i==0)break;
            }
        }
    }
    public static void Heapify(int[]arr,int heapsize,int p){//heapsize记录堆的长度,p则指向是哪一个结点需要堆化
        int child=-1;//记录左右值最大的孩子
        if((p*2+1)<heapsize){
            while ((p*2+1)<heapsize){
                if((p*2+2)<heapsize){
                    child=arr[(p*2+1)]<arr[(p*2+2)]?(p*2+2):(p*2+1);
                }//取两个孩子中最大的一个
                else child=(p*2+1);
                if(arr[p]<arr[child]){
                    swap(arr,p,child);
                    p=child;
                }
                else return;
            }
        }
        else return;
    }

例题
已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离可以不超过k,并且k相对于数组来说比较小。请选择一个合适的排序算法针对这个数据进行排序。

这题思路上比较简单使用小根堆,那么前k个数上一定存在着最小值(移动距离不超过k,k位置移动到0位置最多需要距离k),每次弹出一个数又进来一个数,周而复始就完成排序,时间复杂度为O(N)代码如下(偷个懒不想敲了)
在这里插入图片描述
在java中PriorityQueue是优先级队列,底层实现就是小根堆,具体的使用建议百度

3.比较器

可以自己重写来定义大小顺序,比如说对于一个学生对象,可以id升序、id降序,年龄升序、年龄降序等等这些都可以通过比较器来实现,在比较器中返回负数则第一个数在前,返回正数则第二个数在前、返回0则顺序随便。下面通过一段代码来展示

public class student {//学生类
int id;
int age;
String name;  
}
public static class IdAscend implements Comparator<student>{
        @Override
        public int compare(student o1, student o2) {
            return o1.id-o2.id;
        }
    } 
    //创建一个临时演示的数组,并排序打印
 student[]a={
                new student(4,20,"张三"),
                new student(3,21,"李四"),
                new student(2,22,"王五"),
                new student(1,23,"张大炮"),
        };
        Arrays.sort(a,new IdAscend());
        for (int i=0;i<a.length;i++){
            System.out.println(a[i].toString());
        }

运行的结果如下
在这里插入图片描述

4.桶排序

代码比较复杂又不怎么常用所以这里只简单的放上一个链接
https://blog.youkuaiyun.com/qq_52253798/article/details/122970542

5.排序算法总结

这个总结都来源于b站左程云老师的网课的截图
在这里插入图片描述在这里插入图片描述
1,归并排序的额外空间复杂度可以变成0(1),但是非常难,不需要掌握,有兴趣可以搜“归并排序内部缓存法”,但是使用这个方法会失去稳定性
2,“ 原地归并排序”的帖子都是垃圾,会让归并排序的时间复杂度变成0 (N’ 2)
3,快速排序可以做到稳定性问题,但是非常难,不需要掌握,可以搜 “01stable sort"同时空间复杂度会变为O(N)水平
4,所有的改进都不重要,因为目前没有找到时间复杂度0 (N*logN),额外空间复杂度0(1),又稳定的排序。
5,有一道题目,是奇数放在数组左边,偶数放在数组右边,还要求原始的相对次序不变,碰到这个问题,可以怼面试官。

可以使用综合排序,即在一个排序的问题上采用两种或多种排序的思想,因为时间复杂度的瓶颈出现在大数据样本中,在小数据量中时间复杂度的级别反而没有时间复杂度的常数时间影响大。所以可以将两种或者几种排序拼接起来,发挥各自的优势。


哈希表和有序表

文字描述同样来源于老师的课件
哈希表的简单介绍
1)哈希表在使用层面上可以理解为一种集合结构
2)如果只有key,没有伴随数据value,可以使用HashSet结构(C++中叫UnOrderedSet)
3)如果既有key,又有伴随数据value,可以使用HashMap结构(C++中叫UnOrderedMap)
4)有无伴随数据,是HashMap和HashSet唯一的区别,底层的实际结构是一回事
5)使用哈希表增(put)、删(remove)、改(put)和查(get)的操作,可以认为时间复杂度为0(1),但是常数时间比较大
6)放入哈希表的东西,如果是基础类型,内部按值传递,内存占用就是这个东西的大小
7)放入哈希表的东西,如果不是基础类型,内部按引用传递,内存占用是这个东西内存地址的大小.

有序表的简单介绍
1)有序表在使用层面上可以理解为一种集合结构
2)如果只有key,没有伴随数据va lue,可以使用TreeSet结构(C++中叫OrderedSet)
3)如果既有key,又有伴随数据va lue,可以使用Tr eeMap结构(C++中叫OrderedMap)
4)有无伴随数据,是TreeSet和TreeMap唯 一的区别,底层的实际结构是一回事
5)有序表和哈希表的区别是,有序表把key按照顺序组织起来,而哈希表完全不组织
5)红黑树、AVL树、size-balance-tree和跳表等都属于有序表结构,只是底层具体实现不同
6)放入有序表的东西,如果是基础类型,内部按值传递,内存占用就是这个东西的大小
7)放入有序表的东西,如果不是基础类型,必须提供比较器,内部按引用传递,内存占
用是这个东西内存地址的大小
8)不管是什么底层具体实现,只要是有序表,都有以下固定的基本功能和固定的时间复
杂度

### Unity 中实现触摸控制相机缩放和平移 在 Unity 中,可以通过编写 C# 脚本来实现基于多点触控的手势操作来控制摄像机的缩放和平移。具体来说: 为了实现双手捏合/展开以调整摄像机大小的效果,在 `Update` 方法里检测两个手之间的距离变化并据此改变摄像机参数[^2]。 当涉及到平移时,则是在检测到单个手拖拽屏幕的情况下更新摄像机位置,使视图能够跟随手势移动而相应偏移[^3]。 下面是适用于移动端设备上的简化版代码片段用于演示上述两种交互方式: ```csharp using UnityEngine; public class TouchControl : MonoBehaviour { private Camera mainCamera; void Start() { mainCamera = GetComponent<Camera>(); } void Update() { HandlePinch(); HandlePan(); } void HandlePinch() { if (Input.touchCount == 2) { // 获取当前两根手的信息 Touch touchZero = Input.GetTouch(0); Touch touchOne = Input.getTouch(1); Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition; Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition; float prevMagnitude = (touchZeroPrevPos - touchOnePrevPos).magnitude; float currentMagnitude = (touchZero.position - touchOne.position).magnitude; float difference = currentMagnitude - prevMagnitude; if(mainCamera.orthographic){ mainCamera.orthographicSize += difference * Time.deltaTime * orthographicZoomSpeed; mainCamera.orthographicSize = Mathf.Max(mainCamera.orthographicSize, 0.1f); }else{ mainCamera.fieldOfView -= difference * perspectiveZoomSpeed; mainCamera.fieldOfView = Mathf.Clamp(mainCamera.fieldOfView, 1.0f, 90.0f); } } } void HandlePan(){ if(Input.touchCount==1 && Input.GetTouch(0).phase == TouchPhase.Moved){ Vector2 deltaPos = Input.GetTouch(0).deltaPosition / Screen.dpi; transform.Translate(-deltaPos.x,-deltaPos.y,0); } } } ``` 此段程序展示了如何监听用户的输入事件,并根据这些事件动态修改摄像机属性从而达到预期效果。需要注意的是实际项目可能还需要考虑更多细节比如边界条件处理等[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值