一、线性表
线性表(linear list)是n个具有相同特性的数据元素的有限序列,线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列...
线性表在逻辑上是线性结构,也就是连续的一条直线,但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储
二、顺序表
顺序表时用一段物理地址连续的存储单元依次存放数据元素的线性结构,一般情况下采用数组存储,在数组上完成增删查改
自己实现顺序表,帮助理解ArrayList中方法的底层原理
框架代码:
public interface IList {// 新增元素,默认在数组最后新增void add(int data);// 在 pos 位置新增元素void add(int pos, int data);// 判定是否包含某个元素boolean contains(int toFind);// 查找某个元素对应的位置int indexOf(int toFind);// 获取 pos 位置的元素int get(int pos);// 给 pos 位置的元素设为 valuevoid set(int pos, int value);//删除第一次出现的关键字keyvoid remove(int toRemove);// 获取顺序表长度int size();// 清空顺序表void clear();// 打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的void display();//判满boolean isFull();
}public class MyArrayList implements IList{public int[] elem;public int usedSize;public MyArrayList() {this.elem = new int[10];}@Overridepublic void add(int data) {}@Overridepublic boolean isFull() {}@Overridepublic void add(int pos, int data) {}@Overridepublic boolean contains(int toFind) {return false;}@Overridepublic int indexOf(int toFind) {return 0;}@Overridepublic int get(int pos) {return 0;}@Overridepublic void set(int pos, int value) {}@Overridepublic void remove(int toRemove) {}@Overridepublic int size() {return 0;}@Overridepublic void clear() {}@Overridepublic void display() {}
}public class Test {public static void main(String[] args) {//两种创建对象的方式IList iList = new MyArrayList();MyArrayList myArrayList = new MyArrayList();}
}
add(int data)方法:
add(int data,int pos)方法:
定义一个异常类,用来判断pos位置是否合法
public class PosNotLegalException extends RuntimeException {public PosNotLegalException() {}public PosNotLegalException(String msg) {super(msg);}
}
private 修饰的 checkPosOfAdd() 函数,检测pos,若不合法则抛出异常
private void checkPosOfAdd(int pos) throws PosNotLegalException{if(pos < 0 || pos > usedSize) {throw new PosNotLegalException("pos位置不合法!");}}
从最后一个元素位置开始,将pos之后的元素往后移一位,再将data插入到pos位置,最后usedSize++
@Overridepublic void add(int pos, int data) {//判断位置是否合法try {checkPosOfAdd(pos);}catch(PosNotLegalException e) {e.printStackTrace();}//判满if(isFull()) {this.elem = Arrays.copyOf(elem,2*elem.length);}for (int i = this.usedSize-1; i >= pos; i--) {this.elem[i+1] = this.elem[i];}this.elem[pos] = data;this.usedSize++;}
判断是否包含某个元素contains方法
@Overridepublic boolean contains(int toFind) {for (int i = 0; i < usedSize; i++) {if(this.elem[i] == toFind) {return true;}}return false;}
返回查找元素下标indexOf方法
@Overridepublic int indexOf(int toFind) {for (int i = 0; i < usedSize; i++) {if(this.elem[i] == toFind) {return i;}}return -1;}
获取pos下标位置元素get方法
@Overridepublic int get(int pos) {try {checkPosOfAddGetAndSet(pos);//判断pos位置是否合法,此处pos不能等于usedSize,和判满异常区分}catch(PosNotLegalException e) {e.printStackTrace();}return elem[pos];}private void checkPosOfAddGetAndSet(int pos) throws PosNotLegalException{if(pos < 0 || pos >= usedSize) {throw new PosNotLegalException("get or set 中pos位置不合法!");}}
设置pos下标位置的值为value的set方法(相当于更新元素)
@Overridepublic void set(int pos, int value) {try {checkPosOfAddGetAndSet(pos);}catch(PosNotLegalException e) {e.printStackTrace();}elem[pos] = value;}
删除第一次出现的元素,remove方法
@Overridepublic void remove(int toRemove) {int pos = indexOf(toRemove);//判断该元素是否存在if(pos == -1) {System.out.println("没有要删除的数字!");return;}for (int i = pos; i < usedSize-1; i++) {//将后续元素前移覆盖即可elem[i] = elem[i+1];}this.usedSize--;}
删除所有出现的toRemove元素,removeAll方法
public void removeAll(int toRemove) {int pos = indexOf(toRemove);if(pos == -1) {System.out.println("没有要删除的数字!");return;}int i = 0;while(i < usedSize) {if(elem[i] == toRemove) {for(int j = i; j < usedSize-1; j++) {elem[j] = elem[j+1];}this.usedSize--;}if(elem[i] != toRemove) {i++;}}}
获取顺序表长度,清空顺序表,打印顺序表
@Overridepublic int size() {return usedSize;}@Overridepublic void clear() {/*若顺序表元素为引用类型,需回收内存for (int i = 0; i < usedSize; i++) {elem[i] = null;}*/usedSize = 0;}@Overridepublic void display() {for (int i = 0; i < usedSize; i++) {System.out.print(elem[i] + " ");}System.out.println();}
三、ArrayList的使用
我们查看ArratList的源码
发现其是一个泛型类,创建其对象的两种方法如下:
public static void main(String[] args) {ArrayList<Integer> arrayList = new ArrayList<>();List<Integer> list = new ArrayList<>();//推荐这种}
区别:
3.1 ArrayList的构造
我们查看ArrayList的源码,发现其有三个构造方法:
第一个无参构造方法:
查看源码:
虽然没有给数组分配大小,但是当我们add元素时,却依然能成功:
这是因为:当调用不带参数的构造方法进行add时,会分配大小为10的内存,当内存占满了,会进行1.5倍扩容
第二种,有一个整型参数的构造方法
第三种:
3.2 ArrayList的常见方法
1. boolean addAll(Collection c) 尾插 c 中的元素
public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(10);list.add(12);//自动装箱,int类型变为Integer类型System.out.println(list);List<Integer> list1 = new ArrayList<>(list);System.out.println(list1);}
运行结果:
2. E remove(int index) 删除 index 位置元素
报错为数组越界异常,remove不会进行装箱操作
若想直接输入删除的元素:
public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(10);list.add(12);System.out.println(list);//list.remove(1);//删除1下标,没问题//System.out.println(list);//list.remove(12);//想删除12这个元素,会报错list.remove(Integer.valueOf(12));//将int类型的12,转为Integer类型对象,remove删除值为12的对象}
3. List subList(int fromIndex, int toIndex) 截取部分 list
3.3 Arrays的遍历
ArrayList 可以使用三方方式遍历:for循环+下标、foreach、使用迭代器
public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(10);list.add(11);list.add(12);list.add(13);//第一种打印方式:System.out.println(list);//该方法或其父类中肯定重写了toString方法System.out.println("===fori===");//第二种for循环for (int i = 0; i < list.size(); i++) {System.out.print(list.get(i) + " ");}System.out.println();System.out.println("===foreach===");//第三种foreach循环for(Integer x : list) {System.out.print(x + " ");}System.out.println();System.out.println("===iterator打印===");//第四种 iterator打印Iterator<Integer> it = list.iterator();while(it.hasNext()) {//当it后面还有元素就进入循环System.out.print(it.next() + " ");}System.out.println();System.out.println("===ListIterator,专门打印List===");//第五种 ListIterator,专门打印ListListIterator<Integer> it2 = list.listIterator();while(it2.hasNext()) {System.out.print(it2.next() + " ");}System.out.println();System.out.println("===ListIterator 倒着打印===");//第六种 ListIterator 倒着打印ListIterator<Integer> it3 = list.listIterator(list.size());while(it3.hasPrevious()) {System.out.print(it3.previous() + " ");}}
运行结果:
四、ArrayList具体使用
4.1 简单的洗牌算法
需求:生成52张牌,没有大小王,四种花色("♠", "♥", "♣", "♦"),每种13张,乱序洗牌,3个人轮流抓牌,每人抓5张,最后打印每个人手中牌及剩下的牌
框架:
Card类,声明牌的属性,牌的构造方法,重写toString
public class Card {public int rank;//数字public String suit;//花色public Card(int rank,String suit) {this.rank = rank;this.suit = suit;}@Overridepublic String toString() {return "{" + suit + " " + rank + "}";}
}
Cards类,生成52张牌的方法buyCard
import java.util.ArrayList;
import java.util.List;public class Cards {public static final String[] suits = {"♠", "♥", "♣", "♦"};//4个花色 每种13张public List<Card> buyCard() {List<Card> cardList = new ArrayList<>();//创建泛型类型为Card的顺序表for (int i = 0; i < 4; i++) {//四种花色循环for (int j = 1; j <= 13; j++) {//生成每种花色的1~13数字牌int rank = j;String suit = suits[i];Card card = new Card(rank,suit);//构造牌对象cardList.add(card);//将牌对象添加到顺序表中}}return cardList;}
}
Test类
import java.util.List;public class Test {public static void main(String[] args) {Cards cards = new Cards();//创建牌对象List<Card> cardList = cards.buyCard();//buyCard()返回值类型为List<Card>System.out.println(cardList);}
}
洗牌操作
//Cards类public void shuffle(List<Card> cardList) {Random random = new Random();for (int i = cardList.size()-1; i > 0; i--) {//从后向前循环int randIndex = random.nextInt(i);//生成随机整数,该整数范围:0~i之间swap(cardList,i,randIndex);//交换i下标与生成随机整数下标处的值}}//根据生成随机整数来打乱牌序private void swap(List<Card> cardList,int i,int j) {Card tmp = cardList.get(i);cardList.set(i,cardList.get(j));cardList.set(j,tmp);}
3个人轮流抓牌,每人抓5张
public void drawCard(List<Card> cardList) {List<Card> hand1 = new ArrayList<>();//三个手对象,用来存放每个人每次摸的牌List<Card> hand2 = new ArrayList<>();List<Card> hand3 = new ArrayList<>();List<List<Card>> hands = new ArrayList<>();//创建一个泛型类为List<Card>的对象,用来存放三个人hands.add(hand1);//将三个人放到hans的顺序表中hands.add(hand2);hands.add(hand3);for (int i = 0; i < 5; i++) {//外循环5次,模拟摸5次牌,for (int j = 0; j < 3; j++) {//内循环3次,模拟三个人Card card = cardList.remove(0);//创建Card类型对象,暂存摸出的牌hands.get(j).add(card);//将牌放到相应人的相应手里面}}System.out.println("第一个人的牌" + hand1);System.out.println("第二个人的牌" + hand2);System.out.println("第三个人的牌" + hand3);}
扩展:给摸完牌后的牌排序
4.2 杨辉三角
//杨辉三角public List<List<Integer>> generate(int numRows) {List<List<Integer>> ret = new ArrayList<>();List<Integer> list = new ArrayList<>();list.add(1);ret.add(list);for (int i = 1; i < numRows; i++) {List<Integer> curRow = new ArrayList<>();curRow.add(1);for(int j = 1; j < i; j++) {List<Integer> preRow = ret.get(i-1);int x = preRow.get(j);int y = preRow.get(j-1);curRow.add(x+y);}curRow.add(1);ret.add(curRow);}return ret;}
4.3 面试题:将str1中出现的str2中的相同元素删除
例:
str1:welcome to china
str2:come
要求输出结果:wl t hina
两种解决方式:
//ArrayList实现public static void func2(String str1,String str2) {List<Character> list = new ArrayList<>();//用ArrayList实现该方法,最好使用接口进行对象创建for (int i = 0; i < str1.length(); i++) {char ch = str1.charAt(i);//str2是字符串,ch是字符,为了解决他们用contains方法会报错的问题//if(!str2.contains(ch)) {//将该行代码改为以下,在ch后面+""即可变为String类型if(!str2.contains(ch+"")) {list.add(ch);}}for(char ch : list) {System.out.print(ch);}System.out.println();}//StringBuilder实现public static void func(String str1,String str2) {StringBuilder stringBuilder = new StringBuilder();for (int i = 0; i < str1.length(); i++) {char ch = str1.charAt(i);if(!str2.contains(ch+"")) {stringBuilder.append(ch);}}System.out.println(stringBuilder);}