zoukankan      html  css  js  c++  java
  • JAVA数据结构与算法(一)

    数据结构和算法概述

    数据结构就是把数据元素按照一定的关系组织起来的集合,用来组织和存储数据
    数据结构分为逻辑结构和物理结构两大类

    逻辑结构分类

    a.集合结构:集合结构中数据元素除了属于同一个集合外,他们之间没有任何其他的关系。
    b.线性结构:线性结构中的数据元素之间存在一对一的关系。
    c.树形结构:树形结构中的数据元素之间存在一对多的层次关系
    d.图形结构:图形结构的数据元素是多对多的关系
    

    物理结构分类

    顺序存储结构:把数据元素放到地址连续的存储单元里面,其数据间的逻辑关系和物理关系是一致的 ,比如我们常用的数组就是顺序存储结构。
    链式存储结构:数据元素存放在任意的存储单元里面,这组存储单元可以是连续的也可以是不连续的。此时,数据元素之间并不能反映元素间的逻辑关系,
    因此在链式存储结构中引进了一个指针存放数据元素的地址,这样通过地址就可以找到相关联数据元素的位置。
    

    算法

    根据一定的条件,对一些数据进行计算,得到需要的结果。
    在程序中,我们可以用不同的算法解决相同的问题,而不同的算法的成本也是不相同的。
    总体上,一个优秀的算法追求以下两个目标:
    1.花最少的时间完成需求;
    2.占用最少的内存空间完成需求;

    算法的复杂度分析

    时间复杂度分析

    时间复杂度分析实例

    大O记法规则

    推导大O阶的表示法有以下几个规则可以使用

    1. 用常数1取代运行时间中的所有加法常数;
    2. 在修改后的运行次数中,只保留高阶项;
    3. 如果最高阶项存在,且常数因子不为1,则去除与这个项相乘的常数;

    常见的大O阶


    复杂程度从低到高依次为:O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)<O(n^3)
    我们提到的运行时间都指的是最坏情况下的运行时间.

    空间复杂度分析

    排序算法(笔试题)

    稳定:如果a原本在b前面且a=b,排序之后a仍然在b的前面。
    不稳定:如果a原本在b的前面且a=b,排序之后 a 可能会出现在 b 的后面。
    时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
    空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。

    手撕 冒泡排序

    原理:由上至下,每行对相邻两个元素进行大小比较,若左边的大,则交换位置。

    public class BubbleSort {
        public static void main(String[] args) {
            Integer[] aArray= {4,5,6,3,2,1};
            bubbleSort(aArray);
            System.out.println(Arrays.toString(aArray));
        }
    
        public static void bubbleSort(Comparable[] a){
            for(int i=a.length-1;i>0;i--){
                for(int j=0;j<i;j++){
                    if(Sort(a[j],a[j+1])){
                        exchange(a,j,j+1);
                    }
                }
    
            }
    
        }
        public static boolean Sort(Comparable lTemp, Comparable rTemp){
            return lTemp.compareTo(rTemp)>0;
        }
        public static void exchange(Comparable[] a,int i,int j){
            Comparable temp=a[i];
            a[i]=a[j];
            a[j]=temp;
        }
    }
    

    手撕 快速排序

    package simple;
    
    
    import java.util.Arrays;
    
    /**
     * @Author hwj
     * @Date 2020/8/14 9:41
     * @Desc: 快速排序是对冒泡排序的一种改进。它的基本思想是:
     * 一轮排序:
     * 定义第一个数字为基准,右指针指向最右侧,左指针指向最左侧
     * 先动右指针,当发现比基准小,记录右指针位置
     * 再动左指针,发现比基准大,记录左指针位置
     * 交换左右指针位置元素
     * 再循环先右后左,直至左指针+1>右指针
     * 交换基准元素与右指针
     * 或者右指针-1=0,则数组不变
     *
     * 分隔:
     * 通过一趟排序将要排序的数据分割成独立的两部分,
     * 其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,
     * 整个排序过程可以递归进行,以此达到整个数据变成有序序列。
     **/
    public class QuickSort {
        public static void main(String[] args) {
            Integer[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 8};
            sort(arr);
            System.out.println(Arrays.toString(arr));
        }
        // 主排序方法入口
        public static void sort(Comparable[] arr){
            int lo=0;
            int hi=arr.length-1;
            sort(arr,lo,hi);
        }
        // 分段排序方法入口
        public static void sort(Comparable[] arr,int lo,int hi){
            // 当两个指针相遇,结束递归
            if (hi<=lo){
                return;
            }
            //对数组中,从lo到hi的元素进行切分
            int partition = partition(arr, lo, hi);
            //对左边分组中的元素进行排序
            sort(arr,lo,partition-1);
            //对右边分组中的元素进行排序
            sort(arr,partition+1,hi);
        }
        // 数组切分
        public static int partition(Comparable[] arr,int lo,int hi){
            Comparable key=arr[lo];// 把最左边的元素当做基准值
            int left=lo;// 定义一个左侧指针,初始指向最左边的元素
            int right=hi+1;// 定义一个右侧指针,初始指向左右侧的元素下一个位置
    //进行切分
            while(true){
    //先从右往左扫描,找到一个比基准值小的元素
                while(less(key,arr[--right])){//循环停止,证明找到了一个比基准值小的元素
                    if (right==lo){
                        break;//已经扫描到最左边了,无需继续扫描
                    }
                }
    //再从左往右扫描,找一个比基准值大的元素
                while(less(arr[++left],key)){//循环停止,证明找到了一个比基准值大的元素
                    if (left==hi){
                        break;//已经扫描到了最右边了,无需继续扫描
                    }
                }
                if (left>=right){
    //扫描完了所有元素,结束循环
                    break;
                }else{
    //交换left和right索引处的元素
                    exch(arr,left,right);
                }
            }
    //交换最后rigth索引处和基准值所在的索引处的值
            exch(arr,lo,right);
            return right; //right就是切分的界限
        }
        /*
       数组元素i和j交换位置
       */
        private static void exch(Comparable[] arr, int i, int j) {
            Comparable t = arr[i];
            arr[i] = arr[j];
            arr[j] = t;
        }
        /*
        比较v元素是否小于w元素
        */
        private static boolean less(Comparable v, Comparable w) {
            return v.compareTo(w) < 0;
        }
    }
    
    
    • 代码实现

      1)队列操作

      1. package queue;
        
        import java.util.Scanner;
        
        public class QueueOp {
        
            public static void main(String[] args) {
                ArrayQueue queue = new ArrayQueue(3);
                char key = ' ';
                Scanner scanner = new Scanner(System.in);
                boolean loop = true;
                // 输出队列
                while (loop) {
                    System.out.println("s(show):显示队列");
                    System.out.println("e(exit):退出程序");
                    System.out.println("a(add):添加数据到队列");
                    System.out.println("g(get):从队列取出数据");
                    System.out.println("l(location):显示队列指定位置数据");
                    key = scanner.next().charAt(0);
        
                    switch (key) {
                        case 's':
                            queue.ShowQueue();
                            break;
                        case 'a':
                            System.out.println("输出一个数");
                            int value = scanner.nextInt();
                            queue.addQueue(value);
                            break;
                        case 'g':
                            try {
                                int res = queue.outQueue();
                                System.out.printf("取出的数据是%d
        ", res);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            break;
                        case 'l':
                            int appoint = scanner.nextInt();
                            queue.ShowAppoint(appoint);
                    }
                }
        
        
            }
        
        }
        
        // 初始化
        class ArrayQueue {
            /**
             * 变量声明:
             * 1 数组最大容量
             * 2 队列头
             * 3 队列尾
             * 4 队列数组(一维)
             */
            private int MaxSize;
            private int front;
            private int rear;
            private int[] queue;
        
            public ArrayQueue(int MaxSize) {
                // 对声明的变量进行初始化
                this.MaxSize = MaxSize;
                front = -1;
                rear = -1;
                queue = new int[MaxSize];
            }
        
            // 判断队列是否满
            public boolean isFull() {
                return rear == MaxSize;
            }
        
            // 判断队列是否为空
            public boolean isEmpty() {
                return front == rear;
            }
        
            // 加数据
            public void addQueue(int n) {
                if (isFull()) {
                    System.out.println("队列满,不能加入数据~");
                    return;
                }
                rear++; //让rear后移
                queue[rear] = n;
        
            }
        
            // 取数据
            public int outQueue() {
                if (isEmpty()) {
                    throw new RuntimeException("队列空,不能取数据~");
                }
                front++; //让front后移
                return queue[front];
            }
        
            // 显示队列所有数据
            public void ShowQueue() {
                if (isEmpty()) {
                    throw new RuntimeException("队列空,无数据~");
                }
                int t = -1;
                t++; //让front后移
                System.out.printf("queue [%d]=%d/t", t, queue[t]);
            }
        
            //显示队列指定位置数据
            public void ShowAppoint(int appoint) {
                if (appoint <= -1 || appoint >= MaxSize) {
                    throw new RuntimeException("队列空,无数据~");
                }
                int t = -1;
                t++; //让front后移
                System.out.printf("queue [%d]=%d
        ", t, queue[t]);
            }
        }
        

      2)环形队列

      关键点:

        1)int[] arr 是定义一个整型数组当队列

        2)maxSize是数组的最大容量

        (这里规定,满队列时元素的个数是maxSize-1)

        3)front指向队列的第一个元素,也就是说 array[front] 是队列的第一个元素

        4)rear指向队列的最后一个元素,初值为0

        5)队列满的条件:(rear + 1) % maxSize == front

        队列为空的条件: rear == front

      package queue;
      
      import java.util.Scanner;
      
      public class CircleQueue {
          public static void main(String[] args) {
              //测试一把
              System.out.println("测试数组模拟环形队列的案例~~~");
      
              // 创建一个环形队列
              CircleArray queue = new CircleArray(4); //说明设置4,其队列有效数据最大是3
              char key = ' '; // 接收用户输入
              Scanner scanner = new Scanner(System.in);//
              boolean loop = true;
              // 输出一个菜单
              while (loop) {
                  System.out.println("s(show): 显示队列");
                  System.out.println("e(exit): 退出程序");
                  System.out.println("a(add): 添加数据到队列");
                  System.out.println("g(get): 从队列取出数据");
                  System.out.println("h(head): 查看队列头的数据");
                  key = scanner.next().charAt(0);// 接收一个字符
                  switch (key) {
                      case 's':
                          queue.showQueue();
                          break;
                      case 'a':
                          System.out.println("输出一个数");
                          int value = scanner.nextInt();
                          queue.addQueue(value);
                          break;
                      case 'g': // 取出数据
                          try {
                              int res = queue.getQueue();
                              System.out.printf("取出的数据是 %d
      ", res);
                          } catch (Exception e) {
                              // TODO: handle exception
                              System.out.println(e.getMessage());
                          }
                          break;
                      case 'h': // 查看队列头的数据
                          try {
                              int res = queue.headQueue();
                              System.out.printf("队列头的数据是%d
      ", res);
                          } catch (Exception e) {
                              // TODO: handle exception
                              System.out.println(e.getMessage());
                          }
                          break;
                      case 'e': // 退出
                          scanner.close();
                          loop = false;
                          break;
                      default:
                          break;
                  }
              }
              System.out.println("程序退出~~");
          }
      
      }
      
      
      class CircleArray {
          private int maxSize; // 表示数组的最大容量
          //front 变量的含义做一个调整:front就指向队列的第一个元素,也就是说arr[front]就是队列的第一个元素
          //front 初始值= 0
          private int front;
          //rear 变量的含义做一个调整:rear 指向队列的最后一个元素的后一个位置.因为希望空出一个空间做为约定
          //rear 初始值= 0
          private int rear; // 队列尾
          private int[] arr; // 该数据用于存放数据,模拟队列
      
          public CircleArray(int arrMaxSize) {
              maxSize = arrMaxSize;
              arr = new int[maxSize];
          }
      
          // 判断队列是否满
          public boolean isFull() {
              return (rear + 1) % maxSize == front;
          }
      
          // 判断队列是否为空
          public boolean isEmpty() {
              return rear == front;
          }
      
          // 添加数据到队列
          public void addQueue(int n) {
              // 判断队列是否满
              if (isFull()) {
                  System.out.println("队列满,不能加入数据~");
                  return;
              }
              // 直接将数据加入
              arr[rear] = n;
              // 将rear后移,这里必须考虑取模
              rear = (rear + 1) % maxSize;
          }
      
          // 取数据
          public int getQueue() {
              // 判断队列是否为空
              if (isEmpty()) {
      //            抛出异常
                  throw new RuntimeException("队列空,不能取数据~");
              }
              //这里需要分析出 front 是指向队列的第一个元素
              //1. 先把 front 对应的值保留到一个临时变量
              //2. 将 front 后移,考虑取模
              //3. 将临时保存的变量返回
              int value = arr[front];
              front = (front + 1) % maxSize;
              return value;
      
          }
      
          // 显示队列所有数据
          public void showQueue() {
              // 遍历
              if (isEmpty()) {
                  System.out.println("队列空的,没有数据~~");
                  return;
              }
      //        思路:从front开始遍历,遍历多少个元素
      
              for (int i = front; i < front + size(); i++) {
                  System.out.printf("arr[%d]=%d
      ", i % maxSize, arr[i % maxSize]);
              }
          }
      
          // 求出当前队列有效数据个数
          public int size() {
              // rear = 2
              // front = 1
              // maxSize = 3
              return (rear + maxSize - front) % maxSize;
          }
      
          // 显示队列的数据,注意不是取出数据
          public int headQueue() {
              // 判断
              if (isEmpty()) {
                  throw new RuntimeException("队列空的,没有数据~~");
              }
              return arr[front];
          }
      }
      
    初晨暖阳,夜落星河。 少年披梦,远方有歌。 红黄之上,春夏晚风。 闲肆游走,人群熙攘。
  • 相关阅读:
    github分支规范
    前端工程师为什么要学习编译原理?
    现代编译原理——第六章:中间树 IR Tree 含源码
    现代编译原理——第五章:活动记录
    现代编译原理——第四章:语义分析以及源码
    现代编译原理——第三章:抽象语法树以及源码
    现代编译原理——第二章:语法分析之LL(K)
    现代编译原理——第1章:词法分析
    现代编译原理——第0章
    优雅的数组降维——Javascript中apply方法的妙用
  • 原文地址:https://www.cnblogs.com/alidata/p/13448690.html
Copyright © 2011-2022 走看看