数据结构 - 数组

数据结构 - 数组

1 数组

1.1 自定义动态数组
public class Array<E> {
    //内部维护一个泛型数组,该数组的长度代表容量,少维护一个变量
    private E[] data;
    //size用来表示数组中元素的实际数量
    private int size;
	//无参构造自动创建一个长度为10的数组,构造函数之间的相互调用通过this
    public Array() {
        this(10);
    }
	//初始化size和data
    public Array(int size) {
        this.size = 0;
        this.data = (E[]) new Object[size];
    }

    // 添加数据到指定索引的位置
    public void add(int index, E element) {
        if (index < 0 || index > size)
            throw new IllegalArgumentException("index out of bounds");


        for (int i = size; i > index; i--) {
            data[i] = data[i-1];
        }
        data[index] = element;
        size++;
		//数组元素实际数里达到容量的0.85时扩容两倍
        if (size >= data.length * 0.85)
            resize(data.length << 1);
    }

    //尾插
    public void addLast(E element){
        add(size,element);
    }

    //头插
    public void addFirst(E element){
        add(0,element);
    }

    //删除指定索引的数据
    public E remove(int index) {
        if (index < 0 || index >= size)
            throw new IllegalArgumentException("index out of bounds");



        E e = data[index];
        for (int i = index; i < size-1; i++) {
            data[i] = data[i+1];
        }
        size--;
        data[size] = null;
		// 当数组参数实际数量达到容量的1/4时,缩容为原来的1/2,采用lazy的策略防止复杂度震荡
        // 复杂度震荡即如果size<容量的1/2缩容,而size>容量又扩容,则重复以上操作时间复杂度会很高
        if (size <= data.length>>2 && data.length>>2 != 0)
            resize(data.length>>1);

        return e;
    }

    //删除第一个
    public E removeFirst(){
        return remove(0);
    }

    //删除最后一个
    public E removeLast(){
        return remove(size-1);
    }

    //获取指定下标的元素
    public E get(int index){
        if (index < 0 || index >= size)
            throw new IllegalArgumentException("index out of bounds");
        return data[index];
    }

    //修改指定下标位置的元素
    public void set(int index, E element){
        if (index < 0 || index >= size)
            throw new IllegalArgumentException("index out of bounds");
        data[index] = element;
    }

    //判断是否为数组是否为空
    public boolean isEmpty(){
        return size==0;
    }

    //判断数组是否包含元素
    public boolean contains(E element){
        for (int i = 0; i < size; i++) {
            if (data[i].equals(element))
                return true;
        }
        return false;
    }

    //根据元素查找下标
    public int find(E element) {
        for (int i = 0; i < size; i++) {
            if (data[i].equals(element))
                return i;
        }
        return -1;
    }

    //动态数组,这里为了复用同时实现扩容和缩容
    public void resize(int capacity) {
        E[] arr = (E[]) new Object[capacity];
        for (int i = 0; i < size; i++) {
            arr[i] = data[i];
        }
        data = arr;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("Capacity = "+data.length+", Size = "+size+ "\n"+ "[");

        for (int i = 0; i < size; i++) {
            builder.append(data[i]);
            if (i != size-1)
                builder.append(", ");
        }

        builder.append("]");
        return builder.toString();
    }
}

// 均摊时间复杂度
// - 假设capacity=n,执行n+1次addLast操作,触发resize,n+1次add进行n+1次操作,resize进行n次操作,总共2n+1次操作,平均每次add进行2次操作
1.2 二维数组
  • 数组一般采用顺序存储结构,当需要存储多维数组时,Java是按行存储,先存第一行,再存第二行 ...
// 二维数组下标从0 开始
// 按行存储,找aij的存储位置,假设每行n个元素,前面一共存储了i*n+j 个元素
// 每个元素占L个字节,基地址LOC(a00),也就是元素中第一个元素的地址
LOC(aij) = LOC(a00) + (i*n+j)*L

// 按列存储,找aij的存储位置,假设每列m个元素,前面一共存储了j*m+i 个元素
// 每个元素栈L个字节,基地址LOC(a00)
LOC(aij) = LOC(a00) + (j*m+i)*L    

// 二维数组下标从1 开始 
// 按行存储,找aij的存储位置,假设每行n个元素,前面一共存储了(i-1)*n+j-1 个元素
// 每个元素占L个字节,基地址LOC(a11),也就是元素中第一个元素的地址
LOC(aij) = LOC(a11) + ((i-1)*n+j-1)*L

// 按列存储,找aij的存储位置,假设每列m个元素,前面一共存储了(j-1)*m+i-1 个元素
// 每个元素栈L个字节,基地址LOC(a11)
LOC(aij) = LOC(a11) + ((j-1)*m+i-1)*L        
1.3 特殊矩阵压缩
  • 什么是压缩矩阵?

把多个相同的元素分配一个存储空间,元素为0的不分配空间

  • 什么样的矩阵能够压缩?

特殊矩阵,如对称矩阵,对角矩阵,三角矩阵,稀疏矩阵等

  • 什么是稀疏矩阵?

矩阵中非零元素的个数少(一般认为非零元素个数少于5%的矩阵)

三元组 i j k 以及矩阵行列m*n 存储

1.3.1 对称矩阵
/*
	基本概念
	- 数据沿着对角线对称
	- 根据其对称性,只存储上三角或下三角即可
*/

// 按行序存储下三角,确定aij的位置
// 前面i-1 行一共 (i-1)*i / 2 个元素,第一行1个元素,第二行2个元素 ... 第i-1 行 i-1个元素,等差数列求和
// 第i行有 j-1 个元素
LOC(aij) = LOC(a11) + ((i-1)*i / 2 + j-1)*L
    
// 上三角 i<j
// 下三角 i>=j
// 当要在下三角中寻找存储在上三角中的元素时,可以根据对称性 aij = aji    
    
// n*n 对称矩阵A,下三角按行存放在一维数组B中,A[0][0]存放在B[0]中,求A[i][i]在B数组中的下标
    (i * i+1) / 2 + i

1.3.2 三角矩阵
/*
	基本概念
	- 分为下三角矩阵和上三角矩阵
	- 下三角矩阵指矩阵的下三角有数据,其余部分都是常数C或0(常数c在数组中只占一个位置,0不存储)
*/

// 按行存储下三角,从a11开始存入数组中,其中aij的下表为
	(i - 1)*i / 2 + j - 1 (i >= j) 
	n * (n+1) / 2 (i < j)       
        
// 等差数列求和公式:a1 .... 1n
        (a1 + an)*n / 2
        
// 上三角矩阵按行存储,每行最多n个元素 从a11开始,求aij 的下标位置,第一行n个元素,第二行n-1个 ... 第i-1 行 n-i+2 个
        (n + n-i+2)(i-1) / 2 + j-i	(i <= j)
        n*(n+1) / 2 (i > j)

1.3.3 对角矩阵
// 概念
// 又称带状矩阵,在n*n的矩阵中,非零元素集中在主对角线及其两侧,共L(奇数)条对角线的带状区域内,成为L对角矩阵

//存储时节省空间,第一行前面和最后一行后面的d个0可不存储,掐头去尾,需要L*n - 2d

//求aij前元素个数k,第一个元素为a11,L条对角线
// - d 是掐头,去掉第一排的0,+ d 是对角线左边的元素个数为d个
k = (i-1)*L - d + j - i + d = (i-1)*L + j - i (|i-j|<=d)在带状区域内

3 数组相关例题
/*
	现有n个正整数,n≤10000,
    * 要求出这nn个正整数中的第k个最小整数(相同大小的整数只计算一次),
    * k≤1000,正整数均小于30000
*/
// 解题思路:先排序再去重
        Scanner scanner = new Scanner(System.in);
        String line = scanner.nextLine();
        String[] s = line.split(" ");
            int[] arr = new int[Integer.parseInt(s[0])];
               int k = Integer.parseInt(s[1]);
            line = scanner.nextLine();
            s = line.split(" ");

                for (int j = 0; j < s.length; j++) {
                    arr[j] = Integer.parseInt(s[j]);
                }

                Arrays.sort(arr);
                int c = 0;
                for (int j = 0; j < s.length; j++) {
                    if (j != s.length-1 && arr[j] == arr[j+1])
                        continue;
                    if (++c == k) {
                        System.out.println(arr[j]);
                        break;
                    }
                }

                if (c < k)
                    System.out.println("NO RESULT");
/*
	此题简化后意为需要统计输入的n组数组中和目标数组中相同的元素个数(不考虑顺序),最后在用一个数组来显示结果
	解题思路:
	将固定长度[1-33]声明一个数组,将目标数组中元素和其对应相同的下标加一,再查看n个数组中对应下标的元素是否为0

*/

import java.util.Scanner;

public class P2550 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String line = null;
        int[] res = new int[7];
        int[] com = new int[33];
        int count = 0;
        int end = 0;
        while (true){
            line = scanner.nextLine();
            int g = 0;

            if (count < 1) {
                end = Integer.parseInt(line);
                count++;
                continue;
            }

            if (count == 1) {
                String[] s = line.split(" ");
                for (int i = 0; i < s.length; i++) {
                    com[Integer.parseInt(s[i])]++;
                }
                count++;
                continue;
            }

            if (count++ > 1) {
                String[] s = line.split(" ");
                for (int i = 0; i < s.length; i++) {
                    if (com[Integer.parseInt(s[i])] != 0)
                        g++;
                }
                if (g > 0)
                    res[7-g]++;

                if (count >= end+2)
                    break;
            }

        }

        for (int i = 0; i < res.length; i++) {
            System.out.print(res[i]);
            if (i != res.length-1)
                System.out.print(" ");
        }
    }
}
/*
	输入:
0001000
0001000
0001111
0001000
0001000
0001000
1111111

	输出:
	7 3 1 6 1 6 4 3 1 6 1 6 1 3 7
	
	解题思路:
	首先将字符串拼接,将N行字符串拼接为一个连续的字符串s
	java 中可以直接使用+ 拼接
	遍历字符串,先单独判断第一个字符串是否为1,为一拼接为" 0"
	声明计数变量index,初始值为1,cs用来记录当前字符串为0还是1,遍历字符串,判断下一个值和cs是否相等
	如果相等则index++,继续
	如果不相等,结果拼接" index",index置1,
	最后循环结束后,结果再拼接一次index
*/

import java.util.Scanner;

public class P1320 {


    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String line = null;
        String s = "";
        String res = "";
        int count = 0;
        int end = 0;

        while (true) {
            line = scanner.nextLine();
            if (count < 1) {
                end = line.length();
                s+=line;
                res+=end;
                count++;
                continue;
            }

            count++;
            s+=line;
            if (count>= end)
                break;
        }

        int cs = Integer.parseInt(s.substring(0,1));
        if (cs == 1)
            res+=" "+0;
        int index = 1;
        for (int i = 1; i < s.length(); i++) {
            int parseInt = Integer.parseInt(s.substring(i, i + 1));
            if (cs == parseInt) {
                 index++;
             } else {
                 cs = parseInt;
                 res+=" "+index;
                 index = 1;
             }
        }
        res+=" "+index;
        System.out.println(res);

    }
}
/*
	输入:
10 10 2 3
1 1 5 5
5 5 10 10
3 2
5 5
7 1

	输出:
Y 1 1
Y 2 2
N

	解题思路:
	首先创建多维数组arr[n][4] 
	再分别判断输入的坐标x和y是否处于轰炸范围内
*/

public class P1830 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int count = 0;
        int end = 1;
        int a = 0;
        int p = 0;
        String line = null;
        /*int[] attack = null;*/
        int[][] attack1 = null;

        outer: while (true) {
            if (count < 1) {
                line = scanner.nextLine();
                String[] s = line.split(" ");
                a = Integer.parseInt(s[2]);
                p = Integer.parseInt(s[3]);
                end += a+p;
                /*attack = new int[a*4];*/
                attack1 = new int[a][4];
                count++;

                if (p == 0)
                    break outer;
                continue;
            }

            if (count < a+1){
                line = scanner.nextLine();
                String[] s = line.split(" ");
                /*for (int i = count*4-4; i < count*4; i++) {
                    attack[i] = Integer.parseInt(s[index]);
                    index++;
                }*/
                for (int i = 0; i < 4; i++) {
                    attack1[count-1][i] = Integer.parseInt(s[i]);
                }
                count++;
            }

            int y1;
            int y2;
            if (count >= a+1) {
                y1 = 0;
                y2 = 0;
                line = scanner.nextLine();
                String[] s = line.split(" ");
                for (int i = 0; i < attack1.length; i++) {
                    if ((Integer.parseInt(s[0]) >= attack1[i][0] && Integer.parseInt(s[0]) <= attack1[i][2]) && Integer.parseInt(s[1]) >= attack1[i][1] && Integer.parseInt(s[1]) <= attack1[i][3]) {
                        y1++;
                        y2 = i+1;
                    }
                }

                if (y1 > 0)
                    System.out.println("Y "+y1+" "+y2);
                else
                    System.out.println("N");

                count++;

                if (count >= end)
                    break outer;
            }

        }


    }
}
/*
	解题思路:
	声明n*n的多维数组,并初始化为0
	对应萤石和火把,可以声明一个共用函数light,萤石将输入坐标的上下左右2个距离的位置置1,火把将输入坐标的上下左右1个距离的位置置1
	判断该点是否再数组内
	并判断火把的坐标位置上下左右2个距离的位置置1
	最后遍历数组每个元素并输出0的个数
*/

public class P1789 {

    static int n = 0;
    static int m = 0;
    static int k = 0;
    static int[][] arr = null;

    static void light(int x, int y, int z) {
        for (int i = x-z; i <= x+z; i++) {
            for (int j = y-z; j <= y+z; j++) {
                if (i < 0 || i > n-1 || j < 0 || j > n-1)
                    continue;
                else
                    arr[i][j] = 1;
            }
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int count = 1;
        int end = 1;
        String line = null;


        outer: while (true) {

            if (count < 2) {
                line = scanner.nextLine();
                String[] s = line.split(" ");
                n = Integer.parseInt(s[0]);
                arr = new int[n][n];
                m = Integer.parseInt(s[1]);
                k = Integer.parseInt(s[2]);
                end += m+k;
                count++;
                continue;
            }

            if (count < m+2) {
                line = scanner.nextLine();
                String[] s = line.split(" ");
                int x = Integer.parseInt(s[0])-1;
                int y = Integer.parseInt(s[1])-1;
                light(x,y,1);
                if (x - 2 >= 0)
                    arr[x-2][y] = 1;
                if (x + 2 <= n-1)
                    arr[x+2][y] = 1;
                if (y - 2 >= 0)
                    arr[x][y-2] = 1;
                if (y + 2 <= n-1)
                    arr[x][y+2] = 1;

                count++;

                if (k == 0 && count >= m+2)
                    break outer;
            }

            if (count >= m+2) {
                line = scanner.nextLine();
                String[] s = line.split(" ");
                light(Integer.parseInt(s[0])-1,Integer.parseInt(s[1])-1,2);
                count++;

                if (count > end)
                    break outer;
            }

        }

        int num = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (arr[i][j] == 0)
                    num++;
            }
        }

        System.out.println(num);
    }
}
posted @ 2021-01-31 11:22  Pengc931482  阅读(236)  评论(0)    收藏  举报