数据结构第二章助教总结

  

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

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

  冒泡:基本思想是把大的元素往下沉,小的元素往上冒,时间复杂度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;
        
    }

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

  下面说说链表:

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

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

  

猜你喜欢

转载自www.cnblogs.com/X-huang/p/10591584.html