顺序表一般使用数组实现,数组不仅逻辑连续,其物理也连续,下面我们将实现顺序表的基本操作(增删查改)
一、顺序表的创建
- 创建一个DynamicArray类,Dynamic为动态,因为假设我们创建了一个大小为10的数组,若后期想多增加一个元素,还得重新写,所以动态数组是最佳选择。
- 创建属性size用于存储动态数组的有效元素,创建数组elementData不初始化。
- 创建两个构造方法,一个无参构造(若用户不指定数组大小,则默认创建大小为10的数组);一个参数为整形的有参构造(输入的参数用于定义数组大小)。
- 创建一个方法grow,若数组长度满了之后用于对数组进行扩容。使用private 修饰该方法,因为使用者不需要知道这个方法。
public class DynamicArray {
int[] elementData;
int size;
public DynamicArray(){
elementData=new int[10];
}
public DynamicArray(int length){
elementData=new int[length];
}
/**
* 动态对数组进行两倍扩容
*/
private void grow(){
int oldLength=elementData.length;
int newLength=oldLength << 1;
int[] newArray=new int[newLength];
elementData=Arrays.copyOf(newArray,newLength);
}
二、顺序表添加元素
2.1 头部插入元素(头插法)
- 在插入元素之前我们需要比较size和elementData.length判断数组是否满了,若数组已满,需要调用grow方法对数组进行扩容。
- 在数组中的第一个元素插入元素data,只需要执行elementData[0]=data;但如果数组中第一个元素不为空,那么新插入的元素会将数组的第一个元素覆盖掉。所以我们需要先将数组中的有效元素向后移动一个单位(从后往前移),再插入元素。
- 插入元素后size++,记录数组中有效元素。
/**
* 头部插入元素
* @param data
*/
public void addFirst(int data){
if(size==elementData.length){
grow();
}
for (int i = size-1; i >=0; i--) {
elementData[i+1]=elementData[i];
}
elementData[0]=data;
size++;
}
2.2顺序表尾部插入元素(尾插法)
- 在插入元素之前我们需要比较size和elementData.length判断数组是否满了,若数组已满,需要调用grow方法对数组进行扩容。
- 执行elementData[size]=data;既将data元素插入顺序表最后一个位置。
- 插入元素后size++,记录数组中有效元素。
/**
* 尾部插入元素
* @param data
*/
public void addLast(int data){
if(size==elementData.length){
grow();
}
elementData[size++]=data;
}
2.3在顺序表index位置插入元素
这个方法其实和头插法差不多,就是一个将所有元素往后移然后插入,一个从index之后的元素往后移然后插入元素。
- 比较index和size,若index>size或者index<0;则直接终止函数。
- 在插入元素之前我们需要比较size和elementData.length判断数组是否满了,若数组已满,需要调用grow方法对数组进行扩容。
- 先将数组中的索引为index以后的有效元素向后移动一个单位(从后往前移),再插入元素。
- 插入元素后size++,记录数组中有效元素。
/**
* 索引插入元素
* @param index
* @param data
*/
public void index(int index,int data){
if(index>size || index<0){
System.out.println("插入位置不合法!!!");
return;
}
if(size==elementData.length){
grow();
}
if(index==0){
addFirst(data);
}else if(index==size){
addLast(data);
}else{
for (int i = size-1; i>=index; i--) {
elementData[i+1]=elementData[i];
}
elementData[index]=data;
size++;
}
}
三、顺序表查找
3.1按值查找
- 循环遍历数组,若数组中有值为data的元素,则返回数组的索引;
- 若循环结束,则表示未找到,返回-1 。
/**
* 按值查找
* @param data
* @return
*/
public int findVal(int data){
for (int i = 0; i < size; i++) {
if(elementData[i]==data){
return i;
}
}
System.out.println("没有这个数!!!");
return -1;
}
3.2按索引查找
- 判断索引index是否超出顺序表范围,若超出,直接结束该函数。
- 返回elementData[index] .
/**
* 按索引查找
* @param index
* @return
*/
public int findIndex(int index){
if(index>=size || index<0){
System.err.println("查找位置不合法!!!");
return -1;
}
return elementData[index];
}
四、顺序表的删除
4.1按索引删除
顺序表中的删除操作很简单,比如要删除第2个元素,只需要将后面的元素往前移动一个单位,将其覆盖就行
- 判断索引index是否超出顺序表范围,若超出,直接结束该函数。
- 将index后面的元素向前移动一个单位。
- 将顺序表最后一个元素赋值为0;
- 插入元素后size++,记录数组中有效元素。
/**
* 删除索引位置的值
* @param index
*/
public void delIndex(int index){
if(!content(index)){
System.out.println("该位置没有值!!!");
return;
}
for (int i = index; i <size-1; i++){
elementData[i]=elementData[i+1];
}
size--;
elementData[size]=0;
}
4.2按值删除
4.2.1删除首次出现的元素
- 遍历数组,若有值为data的数组元素,调用delIndex方法删除该元素
/**
* 删除第一次出现的值为data的元素
* @param data
*/
public void delOne(int data){
int index=-1;
for (int i = 0; i < size; i++) {
if(elementData[i]==data){
delIndex(index);
break;
}
}
}
4.2.2删除所有值为data的元素
- 同样遍历数组,若找到值为data的元素,则删除,否则寻找下一个
值得注意的是,这里不能直接用if语句判断,因为若数组是这样[1,9,9,3],删除第一个9后,索引号为2的9索引号变成了1,你的循环又直接往后走了一次,直接判断索引号为2的元素,此时索引号为2的元素值为3,那么就少删除一个2,所以我们应该使用while判断,直到索引号为1的元素的值不是要删除的再往后走。
/**
* 删除数组中所有data
* @param data
*/
public void delVal(int data){
for (int i = 0; i < size; i++) {
while(elementData[i]==data){
delIndex(i);
}
}
}
五、顺序表的修改
5.1按索引号修改
- 判断索引index是否超出顺序表范围,若超出,返回-1。
- 定义一个变量ret记录elementData[index]的值,将data赋值给elementData[index]。
- 返回ret。
/**
* 按索引号修改元素
* @param index
* @param data
* @return
*/
public int changeIndex(int index,int data){
if(index<0 || index>=size){
System.err.println("该位置非法!!!");
return -1;
}else {
int ret=elementData[index];
elementData[index]=data;
return ret;
}
}
5.2按值修改
- 遍历数组,若遇到值为data的元素,直接修改
/**
* 按值修改
* @param data
*/
public void changeVal(int data){
for (int i = 0; i < size; i++) {
if (elementData[i]==data){
elementData[i]=data;
}
}
}
六、顺序表的优缺点分析
6.1优点
- 可以快速存取表中任意位置元素
- 由于顺序表的物理连续及逻辑连续,所以无需为表中的元素间的逻辑关系增加额外的存储空间。
6.2 缺点
- 插入和删除需要移动大量元素
- 当顺序表中长度变化较大时,难以确定存储空间的容量
- 造成存储空间的碎片(比如数组大小为100单位,只使用了60个,那么剩下的40个就浪费掉)
七、顺序表源码
package seqList;
import java.util.Arrays;
public class DynamicArray {
int[] elementData;
int size;
public DynamicArray(){
elementData=new int[10];
}
public DynamicArray(int length){
elementData=new int[length];
}
/**
* 动态对数组进行两倍扩容
*/
private void grow(){
int oldLength=elementData.length;
int newLength=oldLength << 1;
int[] newArray=new int[newLength];
elementData=Arrays.copyOf(newArray,newLength);
}
/**
* 头部插入元素
* @param data
*/
public void addFirst(int data){
if(size==elementData.length){
grow();
}
for (int i = size-1; i >=0; i--) {
elementData[i+1]=elementData[i];
}
elementData[0]=data;
size++;
}
/**
* 尾部插入元素
* @param data
*/
public void addLast(int data){
if(size==elementData.length){
grow();
}
elementData[size++]=data;
}
/**
* 索引插入元素
* @param index
* @param data
*/
public void index(int index,int data){
if(!content(index)){
System.out.println("插入位置不合法!!!");
return;
}
if(size==elementData.length){
grow();
}
if(index==0){
addFirst(data);
}else if(index==size){
addLast(data);
}else{
for (int i = size-1; i>=index; i--) {
elementData[i+1]=elementData[i];
}
elementData[index]=data;
size++;
}
}
/**
* 存在返回true;否则返回false
* @param index
* @return
*/
private boolean content(int index){
if(index<0 || index>size){
return false;
}
return true;
}
/**
* 按值查找
* @param data
* @return
*/
public int findVal(int data){
for (int i = 0; i < size; i++) {
if(elementData[i]==data){
return i;
}
}
System.out.println("没有这个数!!!");
return -1;
}
/**
* 按索引查找
* @param index
* @return
*/
public int findIndex(int index){
if(!content(index)){
System.err.println("查找位置不合法!!!");
return -1;
}
return elementData[index];
}
/**
* 删除索引位置的值
* @param index
*/
public void delIndex(int index){
if(!content(index)){
System.out.println("该位置没有值!!!");
return;
}
for (int i = index; i <size-1; i++){
elementData[i]=elementData[i+1];
}
size--;
elementData[size]=0;
}
/**
* 删除第一次出现的值为data的元素
* @param data
*/
public void delOne(int data){
int index=-1;
for (int i = 0; i < size; i++) {
if(elementData[i]==data){
index=i;
break;
}
}
if(index==-1){
return;
}else {
delIndex(index);
}
}
/**
* 删除数组中所有data
* @param data
*/
public void delVal(int data){
for (int i = 0; i < size; i++) {
while(elementData[i]==data){
delIndex(i);
}
}
}
/**
* 按值修改
* @param data
*/
public void changeVal(int data){
for (int i = 0; i < size; i++) {
if (elementData[i]==data){
elementData[i]=data;
}
}
}
/**
* 按索引号修改元素
* @param index
* @param data
* @return
*/
public int changeIndex(int index,int data){
if(index<0 || index>=size){
System.err.println("该位置非法!!!");
return -1;
}else {
int ret=elementData[index];
elementData[index]=data;
return ret;
}
}
/**
* 清空数组
*/
public void clear(){
elementData=null;
}
/**
* 返回有效数据的长度
* @return
*/
public int getSize(){
return size;
}
@Override
public String toString() {
String ret="[";
for (int i = 0; i < size; i++) {
ret+=elementData[i];
if(i!=size-1){
ret+=", ";
}
}
ret+="]";
return ret;
}
public static void main(String[] args) {
DynamicArray arr=new DynamicArray();
arr.index(0,2);
arr.index(0,2);
arr.index(0,3);
arr.delVal(2);
System.out.println(arr);
}
}
八、顺序表源码(使用泛型)
之前的代码只能创建整形的顺序表,加入泛型便可以存储任意类型。
package linklist;
import java.util.Arrays;
/**
* @author 联想
*/
public class ArrayList<T> {
private Object[] data;
private int size;
public ArrayList(){
data=new Object[77];
}
public ArrayList(int num){
data=new Object[num];
}
public boolean isFull(){
if(size==data.length-1){
return true;
}
return false;
}
public void grow(){
Object[] newArray=new Object[data.length<<1];
newArray= Arrays.copyOf(data,newArray.length);
data=newArray;
}
public void addFrist(T val){
if(isFull()){
grow();
}
if(size==0){
data[0]=val;
size++;
return;
}
for (int i = size; i >0 ; i--) {
data[i]=data[i-1];
}
data[0]=val;
size++;
}
public void addIndex(int index,T val){
if(index<0 || index>size){
System.err.println("该位置不合法!!!");
return;
}
if(size==data.length){
grow();
}
for (int i = size; i >index ; i--) {
data[i]=data[i-1];
}
data[index]=val;
size++;
}
public void addLast(T val){
if(size== data.length){
grow();
}
data[size]=val;
size++;
}
public T findIndex(int index){
if(index<0 || index>=size){
System.err.println("该位置不合法!!!");
return null;
}
return (T) data[index];
}
public T findVal(T val){
for (int i = 0; i < size; i++) {
if(data[i]==val){
return (T) data[i];
}
}
System.err.println("没有这个元素!!!");
return null;
}
public void removeOneVal(T val){
for (int i = 0; i < size; i++) {
if(data[i]==val){
for (int j = i; j <size-1; j++) {
data[j]=data[j+1];
}
data[size-1]=0;
size--;
return;
}
}
}
public void removeAll(T val){
for (int i = 0; i < size; i++) {
while (data[i]==val){
for (int j = i; j <size-1; j++) {
data[j]=data[j+1];
}
data[size-1]=0;
size--;
}
}
}
public T removeIndex(int index){
if(index<0 || index>size){
System.err.println("该位置无数据!!!");
return null;
}
T ret= (T) data[index];
for (int i = index; i <size-1 ; i++) {
data[i]=data[i+1];
}
size--;
return ret;
}
public T modifyIndex(int index,int val){
if(index<0 || index>size){
System.err.println("该位置无数据!!!");
return null;
}
T ret= (T) data[index];
data[index]=val;
return ret;
}
public static void main(String[] args) {
}
@Override
public String toString() {
String ret="[";
for (int i = 0; i < size; i++) {
ret+=data[i];
if(size-1!=i){
ret+=", ";
}
}
ret+="]";
return ret;
}
}