Fork me on GitHub

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\n", 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\n", 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\n", res);
                        } catch (Exception e) {
                            // TODO: handle exception
                            System.out.println(e.getMessage());
                        }
                        break;
                    case 'h': // 查看队列头的数据
                        try {
                            int res = queue.headQueue();
                            System.out.printf("队列头的数据是%d\n", 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\n", 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];
        }
    }
    
posted @ 2020-08-06 20:15  园狐  阅读(263)  评论(0编辑  收藏  举报