数据结构之数组与经典面试题(二)

1、定义

所谓数组,是有序的元素序列。
若将有限个类型相同的变量的集合命名,那么这个名称为数组名。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。用于区分数组的各个元素的数字编号称为下标。数组是在程序设计中,为了处理方便,
把具有相同类型的若干元素按无序的形式组织起来的一种形式。这些无序排列的同类数据元素的集合称为数组。int 的数组你就不能存float
也不能存double。数组是用于储存多个相同类型数据的集合。通常用Array表示,也称之为线性表,画图演示
在这里插入图片描述

2、特点

(1)数组是相同数据类型的元素的集合。 (2)数组中的各元素的存储是有先后顺序的,它们在内存中按照这个先后顺序连续存放在一起。内存地址
(3)数组元素用整个数组的名字和它自己在数组中的顺序位置来表示。例如,a[0]表示名字为a的数组中的第一个元素,a[1]代表数组a的第二个元素,以此类推,

(4)随机访问(查找)
数组是连续的内存空间和相同类型的数据。正是因为这两个限制,它才有了一个非常重要的特性:随机访问。但有利就有弊,这两个限制也让数组的很多操作变得非常低效,比如要想在数组中删除、插入一个数据,为了保证连续性,就需要做大量的数据搬移工作。
(5)数组的缺点:插入和删除 实现代码:
设数组的长度为n,现在,如果我们需要将一个数据插入到数组中的第k个位置。删除第N个位置的数据.那么需要移动删除和插入后面的数组元素
(6)使用数组一定要注意访问越界问题;所以一定要多加判断,尤其是在开始和结束。测试的时候也一样注意头和尾。

3.表现形式

(1)一维数组
int a[],String a[] (2)多维数组
int a[][],int a[][][]。 int a[m][n]:内存空间是多少? m*n a[0][10]:
链表解决,a[0]:->10>2 a[1]->15

4、ArrayList和数组

本质是一样的,都是数组。ArrayList是JDK封装了。不需要管扩容等操作;数组的话就要你全部操作 两者选择:
不知道数据大小的肯定选ArrayList。
如果你知道数据的大小而且你又非常关注性能那就用数组。

5、二维数组的内存地址是怎么样的?写出寻址公式?

一维:a[] = new int[10]; ==> loc =
init_loc(初始内存地址)+index(数组的下标)size(数据的长度) 二维=>转化一维 1 2 3 4 5 6 =>
1 2 3 4 5 6 => 4的下标在二维里面是 (1 ,0) =>在一维里面是第3个。=> i
n(一维的长度)+j(在列
)=>13+0=3 a[i][j]: (i<n,j<m) loc=init_loc+(in+j)*size

6、总结:

数组是一个最基础最简单的数据结构,必须要完全搞懂。它是存储相同类型的一组数据,最大的两个特点就是下标和随机访问。缺点就是插入和删除是很慢的,时间复杂度为O(n)。

7、经典面试
7.1 、例子:手写ArrayList
/**
 * @author zhz
 * @date 2020/11/11
 **/
public interface MyList {
    void add(int index, Object element);
    int size();
    void add(Object element);
    void remove(int index);
    Object get(int index);
    String toString();
    void ensureCapacityInternal();
}


import com.hr.tuling.array.MyList;
import java.util.Arrays;
/**
 * @author zhz
 * @date 2020/11/11
 **/
public class MyArrayList implements MyList {

    /**
     * 定义一个数组,用于保存集合中的数据
     */
    private Object[] elementData;

    /**
     * 定义一个变量,用于保存数组中实际存放元素的个数
     */
    private int size;

    /**
     * 获取数组中实际存放元素的个数
     *
     * @return
     */
    public int size() {
        return this.size;
    }

    /**
     * 无参构造方法(默认设置elementData数组的空间长度为10)
     */
    public MyArrayList() {
        this.elementData = new Object[10];
    }

    /**
     * 有参构造方法(指定设置elementData数组的空间长度)
     *
     * @param cap 需要设置elementData的空间长度
     */
    public MyArrayList(int cap) {
        //1、判断cap是否合法
        if (cap < 0) {
            throw new RuntimeException("参数不合法,cap:" + cap);
        }
        //2、实例化elementData数组
        this.elementData = new Object[cap];
    }

    /**
     * 添加元素
     *
     * @param element 需要添加的元素
     */
    @Override
    public void add(Object element) {
        //1、判断数组是否需要扩容
        ensureCapacityInternal();
        //2、把element添加进入数组中
        elementData[size] = element;
        //3、更新size的值
        size++;
    }

    /**
     * 根据索引获取元素值
     *
     * @param index 索引值
     * @return 数组中index索引对应的元素值
     */
    @Override
    public Object get(int index) {
        //1、判断索引是否合法,合法的取值范围:【0,size-1】
        rangeCheck(index);
        //2、根据索引获取对应的元素值
        return elementData[index];
    }

    /**
     * 根据索引删除元素
     *
     * @param index 索引值
     */
    @Override
    public void remove(int index) {
        //1、判断索引是否合法,合法的取值范围:【0,size-1】
        rangeCheck(index);
        //2、把删除索引之后的元素往前移动一位
        //2.1先获得删除索引及其之后的所有索引值
        for (int i = index; i < size; i++) {
            //2.2把后一个元素往前移动一位
            elementData[i] = elementData[i + 1];
        }
        //3、把最后一个实际添加的元素设置为默认值
        elementData[size - 1] = null;
    }

    /**
     * 根据索引插入元素
     *
     * @param index   插入元素的索引位置
     * @param element 需要插入的元素
     */
    @Override
    public void add(int index, Object element) {
        //1、判断索引是否合法,合法的取值范围:【0,size】-->插入的元素可以在实际添加元素的最末尾
        if (index < 0 || index > size) {
            throw new ArrayIndexOutOfBoundsException("索引越界异常,index:" + index);
        }
        // 2.判断数组是否需要扩容
        ensureCapacityInternal();
        // 3.插入索引及其之后的元素往后挪动一位(从后往前挪动)
        // 3.1获得插入索引及其之后的所有索引值
        for (int i = size - 1; i >= index; i--) {
            // 3.2把前一个元素往后挪动一位
            elementData[i + 1] = elementData[i];
        }
        // 4.在插入索引位置实现赋值操作
        elementData[index] = element;
        // 5.更新size的值
        size++;
    }

    /**
     * 检查索引是否合法(get和remove)
     *
     * @param index
     */
    private void rangeCheck(int index) {
        //1、判断索引是否合法,合法的取值范围:【0,size-1】
        if (index < 0 || index >= size) {
            throw new ArrayIndexOutOfBoundsException("索引越界异常,index:" + index);
        }
    }

    /**
     * 判断数组是否需要执行扩容操作
     */
    public void ensureCapacityInternal() {
        //1.1、当数组的空间长度等于数组实际存放元素的个数时,这时就需扩容扩容操作
        if (elementData.length == size) {
            //1.2、创建一个比原数组空间长度更大的新数组
            Object[] newArr = new Object[elementData.length * 2 + 1];
            //1.3、把原数组中的元素拷贝进入新数组中
            for (int i = 0; i < size; i++) {
                newArr[i] = elementData[i];
            }
            //1.4、让原数组保存新数组的地址值
            elementData = newArr;
        }
    }

    @Override
    public String toString() {
        return "ArrayList{" +
                "elementData=" + Arrays.toString(elementData) +
                '}';
    }

    public static void main(String[] args) {
        MyList myList = new MyArrayList(7);
        myList.add(2);
        myList.add(3);
        myList.add(2);
        myList.add(3);
        System.out.println(myList.get(2));
    }
}

7.2、数组的反转

import java.util.Arrays;

/**
 * 数组的反转
 * 例如:数组{11, 22, 33, 44, 55, 66}反转后为{66, 55, 44, 33, 22, 11}
 * @author zhz
 * @date 2020/10/03
 **/
public class 数组的反转 {
    /**
     *  方法一:
     *      1)新建数组,用于保存反转后的结果
     *      2)把需要反转数组中的元素倒序存入新数组中
     */
    public static int[] reverseOrderArray(int[] arr){
        // 1.定义一个新数组,用于保存反转之后的结果
        int[] temp=new int[arr.length];
        // 2.把arr数组中的所有元素倒序的存入temp数组中
        // 2.1通过循环获得arr数组中的每一个元素
        for (int i = arr.length-1; i >= 0; i--) {
            // 2.2把arr数组中的元素倒序存入temp数组中
            temp[arr.length-1-i]=arr[i];
        }
        // 3.把反转之后的数组返回
        return temp;
    }
    /**
     *  方法二:把需要反转数组的元素首尾交换即可
     */
    public static void reverseOrderArray1(int[] arr){
        // 1.通过循环,获得数组前半部分的元素
        for (int i = 0; i <arr.length/2; i++) {
            // 2.把arr[i]和arr[arr.length - 1 - i]做交换
            int temp=arr[i];
            arr[i]=arr[arr.length-1-i];
            arr[arr.length-1-i]=temp;
        }
    }

    public static void main(String[] args) {
        int[] arr={11,22,33,44,55,66};
        //System.out.println(Arrays.toString(数组的反转.reverseOrderArray(arr)));
        数组的反转.reverseOrderArray1(arr);
        System.out.println(Arrays.toString(arr));
    }
}

7.3、 找数组中重复的元素

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author zhz
 * @date 2020/10/03
 **/
public class 找数组中重复的元素 {
    /**
     * 问题:在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,
     * 但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。
     * 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2
     *
     * 解决:第一种,用map做计数器,当大于1时,则返回true,否则false
     */
    public static boolean duplicate(int numbers[],int length,int [] duplication) {
        Map<Integer, Integer> map = new HashMap<>();
        int count=1;
        // 1.判断arr为null或arr.length等于0的情况
        if(numbers == null || length == 0) {
            return false;
        }
        for (int i = 0; i < length; i++) {
            // 3.判断数组元素是否合法
            if(numbers[i] < 0) {
                return false;
            }
            if (!map.containsKey(numbers[i])){
                map.put(numbers[i],count);
            }else{
                map.put(numbers[i],map.get(numbers[i])+1);
            }
        }
        for (Map.Entry<Integer,Integer> entry : map.entrySet()){
            if (entry.getValue()>1){
                duplication[0]=entry.getKey();
                return true;
            }
        }
        return false;
    }

    /**
     * 题目:在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数
     * 组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。
     * 请找出数组中任意一个重复的数字。
     *
     * 解决:用set去判重
     */
    public int findRepeatNumber(int[] nums) {
        Set<Integer> set=new HashSet<>();;
        int res=0;
        for (int num : nums) {
            if (!set.contains(num)) {
                set.add(num);
            }else {
                res=num;
            }
        }
        return res;
    }

    public static void main(String[] args) {
        找数组中重复的元素.duplicate(new int[]{
                0, 3, 4, 1, 4, 8
        },6,new int[]{1});
    }
}

7.4、使奇数位于偶数前面

import java.util.Arrays;

/**
 * 使奇数位于偶数前面
 * 输入一个整型数组,实现一个方法来调整该数组中的元素的顺序,
 * 使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。
 * @date 2020/10/03
 **/
public class 使奇数位于偶数前面 {

    /**
     * 使奇数位于偶数前面
     * @param array 需要调整奇偶数位置的数组
     */
    public static void reOrderArray(int[] array) {
        // 1.处理arr为null的情况
        if (array == null) {
            throw new NullPointerException("空指针异常,array:" + array);
        }
        // 2.定义两个下标,min的初始值为0,max的初始值为arr.length - 1
        int min = 0;
        int max = array.length - 1;
        // 3.定义一个循环,用于调整数组中奇偶数的位置
        while (min < max) {// 如果min小于max,则一直调整数组中元素的位置
            // 4.让min从前往后找,如果arr[min]的值为偶数,则停止查找
            while (min < max && array[min] % 2 != 0) {
                min++;
            }
            // 5.让max从后往前找,如果arr[max]的值为奇数,则停止查找
            while (min < max && array[max] % 2 == 0) {
                max--;
            }
            // 6.如果min的值不等于max,则交换arr[min]和arr[max]的值
            if (min != max) {
                int temp = array[min];
                array[min] = array[max];
                array[max] = temp;
            }
        }
    }

    /**
     *新开一个数组空间
     * @param nums
     * @return
     */
    public int[] exchange(int[] nums) {
        if (nums==null||nums.length==0){
            return nums;
        }
        int left=0;
        int right=nums.length-1;
        int[] res=new int[nums.length];
        for (int i = 0; i < nums.length; i++) {
            if ((nums[i]&1)==0){//偶数
                res[right--]=nums[i];
            }else{
                res[left++]=nums[i];
            }
        }
        return res;
    }

    public static void main(String[] args) {
        int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9};
        使奇数位于偶数前面.reOrderArray(array);
        System.out.println(Arrays.toString(array));
    }
}

我是小白弟弟,一个在互联网行业的小白,立志成为一名架构师
https://blog.csdn.net/zhouhengzhe?t=1

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhz小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值