• 数据结构第二章助教总结


      

      作为有意向成为一名程序员的我,到大三下因为做助教才开通博客,惭愧惭愧。好吧,入正题,说说对大一师弟师妹数据结构作业的一些感悟。

      数据结构第二章讲的是线性表。对于线性表无非就是顺序表(数组)和链表,数组因为其地址在空间连续,可以进行随机存取,但是也因为连续,在进行增删的时候,需要进行数组元素的移动。其实对于数组来说,经常用来操作的就是对数组元素进行排序,然后对排好序的数组进行某些骚操作(一看到有序数组,脑海就出现了二分查找)。排序方法还是有很多的,冒泡、插入、归并、快排。那就来回顾一下吧。

      冒泡:基本思想是把大的元素往下沉,小的元素往上冒,时间复杂度O(n2)。代码贴上

    public int[] maoPao(int[] srcArray) {
            
            // 只需循环srcArray.length - 1次,不需要第length次,因为比他大的都沉下去了,不用比
            for (int i = 0; i < srcArray.length - 1; i++) {
                
                // 一轮下来把最大的放到了最后
                for (int j = 0; j < srcArray.length - 1; j++) { 
                    // 比后一个元素大,往下沉
                    if (srcArray[j] > srcArray[j+1]) {
                        int tmp = srcArray[j];
                        srcArray[j] = srcArray[j+1];
                        srcArray[j+1] = tmp;
                    }
                }
            }
            
            return srcArray;
        }

      插入:每次取一个元素,插入到之前已排序好的部分数组中,时间复杂度O(n2)代码贴上

    public int[] insertSort(int[] srcArray) {
            
            for (int i = 1; i < srcArray.length; i++) {
                
                int tmp = srcArray[i];
                for (int j = i - 1; j >= 0; j--) {
                    // 从部分有序数组从后往前取数据,只要找到第一个小于tmp的,插入进去,就可以跳出这层循环
                    if (tmp < srcArray[j]) {
                        srcArray[j + 1] = srcArray[j];
                        srcArray[j] = tmp;
                    }else {
                        srcArray[j + 1] = tmp;
                        break;
                    }
                }
            }
            
            return srcArray;
        }

      归并:基本思想分治,划分为更小更易求解的子集,然后将结果一步步合并,最终合并成有序数组,时间复杂度为O(nlogn),代码贴上

    public void mergeSort(int[] rtArray, int[] srcArray, int left, int right) {
    
            // 边界条件当右边大于左边时,进行分治归并
            if (left < right) {
                int mid = (left + right) / 2;
                mergeSort(rtArray, srcArray, left, mid);
                mergeSort(rtArray, srcArray, mid + 1, right);
                merge(rtArray, srcArray, left, mid, right);
            }else {
                // 当待排序数组只有一个元素,不用分治,直接copy返回,该判断放在主函数比较好,当待排序数组元素<=1时,
                // 直接返回,不用进入分治归并函数;放在函数体内每次分治到一个元素时都会进行无用功赋值,为什么说是无用功,
                // 因为赋值后每次都会回到上一次函数调用,然后进入merge重新赋值
                if (left == right) {
                    rtArray[left] = srcArray[left];
                }
            }
        }
        
        public void merge(int[] c, int[] srcArray, int left, int mid, int right) {
            
            int indexa = left;
            int indexb = mid + 1;
            int indexc = left;
            
            while (indexa <= mid && indexb <= right) {
                if (srcArray[indexa] < srcArray[indexb]) {
                    c[indexc++] = srcArray[indexa++];
                }else {
                    c[indexc++] = srcArray[indexb++];
                }
            }
            
            while (indexa <= mid) {
                c[indexc++] = srcArray[indexa++];
            }
            while (indexb <= right) {
                c[indexc++] = srcArray[indexb++];
            }
            // 把部分有序的元素放回原始数组,进而影响下一轮的归并
            for (int i = left; i <= right; i++) {
                srcArray[i] = c[i];
            }
        }

      快排:基本思想是在数组中取一个元素,把小于该元素的其他元素放在该元素左边,大于的放在右边,时间复杂度为O(nlogn),代码

      public void quikSort(int[] srcArray, int start, int end) {
            
            if (start < end) {
                // 拿到分割点
                int partication = dividePoint(srcArray, start, end);
                quikSort(srcArray, start, partication); 
                quikSort(srcArray, partication + 1, end);
            }
        }
        
        public int dividePoint(int[] srcArray, int start, int end) {
            
            int key = srcArray[start];
            int begin = start;
            while (begin < end) {
                // 从后往前比较是因为我们移动元素的时候会把原来的元素覆盖掉,从后往前覆盖掉的是首元素(用于比较的元素),
                // 因为我们事先已经把它保存在key中,所以不影响
                while (begin < end && srcArray[end] >= key) {
                    end--;
                }
                srcArray[begin] = srcArray[end]; // 从后往前找到一个小于key的,移到左边
                while (begin < end && srcArray[begin] <= key) {
                    begin++;
                }
                srcArray[end] = srcArray[begin]; // 从后往前找到一个大于key的,移到右边
            }
            srcArray[begin] = key;  // 将key放到begin处,此时的begin为分割点
            return begin;
            
        }

      对于二分查找,难点在于边界的确定,边界问题我也是一直很头疼,一般对边界问题处理我是拿简单的例子代进去尝试来确定是 小于还是小于等于(被自己菜哭)。

      下面说说链表:

      链表的增删很方便,查询不方便,链表元素插入有头插法和尾插法,然后链表较经常用的操作应该是反转链表(或者逆序遍历链表元素),对于逆向遍历链表,如果使用额外空间,使用这一数据结构可以很方便的实现逆序遍历链表;如果不使用额外空间,可以直接把链表反转,改变指针指向。暂时没有想到链表还有哪些操作。。。

      借着做助教这个机会重温一遍数据结构,夯实自己的基础,也希望自己可以给师弟师妹更多的帮助。

      

  • 相关阅读:
    【算法】Kruskal算法(解决最小生成树问题) 含代码实现
    POJ 1182 食物链 (并查集解法)(详细注释)
    APICloud关闭Key Building Resolve
    ubuntu配置国内源
    缓存穿透、缓存击穿、缓存雪崩概念及解决方案
    POST请求和GET请求的区别
    ibatis 中#和 $ 符号的区别
    自动装箱和自动拆箱理解
    回文串算法说明(带注释)
    Object 对象有哪些方法?
  • 原文地址:https://www.cnblogs.com/X-huang/p/10591584.html
Copyright © 2020-2023  润新知