数据结构
首先,我们来了解一下什么是数据结构?为什么要学习数据结构?
一、什么是数据结构?
1、是一门基础学科
2、它研究的是数据如何在计算机中进行组织和存储,使得我们可以高效的获取数据和修改数据
3、数据结构可以分为三类:
- 线性结构:数组、队列、栈、链表、哈希表…
- 树形结构:二叉树、二分搜索树、AVL树、红黑树、堆、Trie树、线段树、并查集…
- 图结构:邻接矩阵、邻接表
4、为什么要学习数据结构?
根据不同的应用,灵活选择最合适的数据结构
数据结构+算法=程序
5、使用环境
IDEA、JDK8+
二、接下来进入我们数据结构的第一个内容——数组
首先让我们回顾一下Java数组的使用
public static void main(String[] args) {
/**
* 数组的概念
* 数组:是用来存储一组**相同类型**的数
* Java中数组的定义:
* 数组类型[] 数组名
* 初始化数组:1、数据类型[] 数组名 = new 数据类型[数组长度];
* 2、数组类型[] 数组名 = {和数组类型同类型的数}
* 代码的限制:在方法函数中,有长度限制,
* 圈复杂度:嵌套不能超过6层
*/
//遍历数组
String[] str ={"chi","he","wan","shui"};
/*一般情况下是for循环遍历*/
for (int i = 0; i < str.length; i++) {
System.out.println(str[i]);
}
/*用流优化数组遍历*/
//将数组转成流 遍历数组
Arrays.stream(str).forEach(System.out::println);//第一种方法
Arrays.stream(str).forEach(item->System.out.println(item));//第二种方法
}
public static void main(String[] args) {
/*数组的使用*/
String[] fruits = {"apple", "banana", "pear", "watermelon", "Pineapple"};
/*for循环
for (int i = 0; i < fruits.length; i++) {
System.out.println(fruits[i]);
}*/
// 使用流遍历,先将数组转成流Arrays.stream(数组名)再用forEach()进行遍历
Arrays.stream(fruits).forEach(System.out::println);
// ->箭头函数
Arrays.stream(fruits).forEach(item -> System.out.println(item));
/*创建一个数组就得开辟空间*/
int[] num = new int[100];
// 1、对每个数据+10
// 2、过滤奇数
/*生成一个随机数*/
Random random = new Random();
for (int i = 0; i < 100; i++) {
num[i] = random.nextInt(100000);
}
/*map()对每个数字进行操作,filter()过滤器, 流:只流一次,可以把所有的操作做完*/
Arrays.stream(num).map(item->item+10).filter(item->item%2>0).forEach(System.out::println);
}
在Java数组中经常会出现两个问题:空指针异常问题()和数组越界问题,所以在数据结构中需要对它的边界进行处理。
统计每个字符出现的次数
方法一:
public static void main(String[] ag){
String str = "aaabbbbcccsssddd";
//统计每个字符出现的次数
//新建一个map集合
Map<Character, Integer> map = new HashMap<>();
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
//如果集合中不包含该键说明它是第一次出现
if (!map.containsKey(c)) {
//向集合中添加键值对put(键,值)
map.put(c, 1);
} else {
//get()方法是返回指定键所对应的值
map.put(c, map.get(c) + 1);
}
}
//{a=3, b=4, c=3, s=3, d=3}
System.out.println(map);
方法二:
public static void main(String[] args) {
/*出现的次数*/
String str = "abanhsbckcnncb";//字符串中只包含小写字母
//英文字母26个
int[] counts = new int[26];
/*
1、先用str.charAt()将获取字符串中的元素(或者用str.toCharArray()将字符串转成数组)
*/
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
//利用编码来统计字母出现的次数
counts[c - 'a']++;
}
//用增强for循环来遍历
for (int item : counts){
System.out.println(item);
}
}
三、数组的数据结构
使用数组时,最重要的就是数组的索引,通过索引可以对数组进行增删改查操作。
索引: 可以有语义,也可以没有语义
数组最大的优点:根据索引快速查询。
数组最好应用于索引有语义的情况。并非所有有语义的数字都可以作为数组的索引,例如:
610258122312033366
数组也是可以处理“索引没有语义”的情况。
产生的问题是:
基于Java中的数组,进行二次封装,制作属于我们自己的数组MySelfArray(可变数组)
创建一个数组
//数组容器,这里我们用到了泛型
T[] data;
//实际保存数据的个数
int size;
public MySelfArray() {
this(20);//数组的初始长度
}
public MySelfArray(int capacity) {
//初始化
this.size = 0;
//开辟指定长度的数组
data = (T[]) new Object[capacity];
}
判断数组是否为空
/**
* 判断数组是否为空
*
* @return boolean
*/
public boolean isEmpty() {
/* if(data.length==0){
return true;
}
return false;*/
return this.size == 0;
}
向数组里添加元素
/**
* 向数组的尾部添加元素
*
* @param ele 要添加的元素
*/
public void addDataTail(T ele) {
/* this.data[size] = ele;
this.size++;*/
add(size, ele);
}
/**
* 向数组头部添加元素
*/
public void addDataHead(T ele) {
/* for (int i = 0; i < size-1; i++) {
data[i] = data[i+1];
}
data[0]=ele;
this.size++;*/
add(0, ele);
}
/**
* 向指定位置添加元素
*
* @param index 索引
* @param ele 要传入的元素内容
*/
public void add(int index, T ele) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("index is error");
}
//如果数组已满,进行扩容操作
resize(2 * data.length);
for (int i = size-1; i >=index; i--) {
data[i+1] = data[i];
}
data[index] = ele;
//更新数组
this.size++;
}
获取指定位置的元素
/**
* 获取指定位置的元素
*
* @param index 索引
* @return 指定位置的元素
*/
public T getEle(int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("index is out of XX");
}
return data[index];
}
修改指定位置的元素
/**
* 修改指定位置的元素
*
* @param index 索引
* @param ele 内容元素
*/
public void setIndexEle(int index, T ele) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("index is out of XX");
}
data[index] = ele;
}
判断是否包含某个元素
/**
* 判断是否包含某个元素
*
* @param ele 内容元素
* @return 是否存在该元素, 并返回该元素的位置
*/
public int isContians(T ele) {
for (int i = 0; i < size; i++) {
if (data[i] == ele) {
return i;
}
}
return -1;
}
删除数组中的元素
/**
* 删除指定位置的元素
*
* @param index 索引
* @return int
*/
public T removeIndexEle(int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("index is invalid!");
}
T reasult = data[index];
for (int i = index; i < size; i++) {
data[i] = data[i + 1];
}
this.size--;
data[size] = null;
//进行缩容操作
if (this.size == data.length / 2 || data.length / 2 > 0) {
resize(data.length / 2);
}
return reasult;
}
/**
* 删除指定的元素
* @param ele 内容
* @return 索引
*/
public int removeEle(T ele){
int index = isContians(ele);
if(index!=-1){
removeIndexEle(index);
}
return index;
}
/**
* 删除头元素
*
* @param index 索引
*/
public void removeHead(int index) {
/*for (int i = index; i < size; i++) {
data[i+1]=data[i];
}
this.size--;
data[size]=0;*/
removeIndexEle(0);
}
数组扩容
/**
* 数组扩容,更改数组容积
*
* @param newCapacity 新的数组长度
*/
public void resize(int newCapacity) {
if (this.size == data.length) {
T[] newData = (T[]) new Object[newCapacity];
for (int i = 0; i < this.size; i++) {
newData[i] = data[i];
}
this.data = newData;
}
}
基本操作
@Override
public String toString() {
return "data=" + Arrays.toString(data);
}
public T[] getData() {
return data;
}
//获取数组中实际保存元素的个数
public int getSize() {
return size;
}
public void setData(T[] data) {
this.data = data;
}
public void setSize(int size) {
this.size = size;
}
注意: 使用泛型(“任意”类型的数组)
- 不可以是基本数据类型(8种 byte char boolean int short float double long),只能是类对象
- 每种基本数据类型都有一个对应的包装类