欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 创投人物 > Java数据结构——第 2 章线性表学习笔记

Java数据结构——第 2 章线性表学习笔记

2025/6/20 20:33:06 来源:https://blog.csdn.net/2302_79239614/article/details/148773341  浏览:    关键词:Java数据结构——第 2 章线性表学习笔记

一、线性表的定义与逻辑结构

1.1 线性表的基本概念

  • 定义:线性表是具有相同特性数据元素的一个有限序列,逻辑结构表示为 (a₀, a₁, ..., aᵢ, ..., aₙ₋₁)
  • 三大特性
    • 所有数据元素类型相同
    • 元素个数有限
    • 每个元素有唯一序号(从 0 开始)
  • 逻辑序号约定:为方便算法设计,逻辑序号与存储序号统一从 0 开始,n 个元素的序号范围为 0≤i≤n-1

1.2 线性表的抽象数据类型(ADT)

 

ADT List {

数据对象:D = {aᵢ | 0≤i≤n-1, n≥0, aᵢ为E类型}

数据关系:r = {<aᵢ, aᵢ₊₁> | aᵢ, aᵢ₊₁∈D, i=0,1,...,n-2}

基本运算(11个):

void CreateList(E[] a); // 由数组创建线性表

void Add(E e); // 末尾添加元素

int size(); // 求表长

void Setsize(int nlen); // 设置表长

E GetElem(int i); // 获取序号i的元素

void SetElem(int i, E e); // 设置序号i的元素

int GetNo(E e); // 查找元素e的序号

void swap(int i, int j); // 交换序号i和j的元素

void Insert(int i, E e); // 在i位置插入元素

void Delete(int i); // 删除i位置的元素

String toString(); // 线性表转字符串

}

二、线性表的顺序存储结构(顺序表)

2.1 顺序表的存储结构

  • 物理实现:用数组data存储元素,size记录当前长度,capacity为数组容量
  • 核心类定义
 

public class SqListClass<E> {

final int initcapacity = 10; // 初始容量

public E[] data; // 存储元素的数组

public int size; // 当前表长

private int capacity; // 数组容量

public SqListClass() { // 构造方法

data = (E[]) new Object[initcapacity];

capacity = initcapacity;

size = 0;

}

// 其他方法...

}

2.2 顺序表基本运算的实现

2.2.1 核心辅助方法:动态扩容
 

private void updatecapacity(int newcapacity) {

E[] newdata = (E[]) new Object[newcapacity];

for (int i=0; i<size; i++) { // 复制原数组元素

newdata[i] = data[i];

}

data = newdata; // 切换到新数组

capacity = newcapacity; // 更新容量

}

  • 作用:当插入导致空间不足时扩容,或删除后空间浪费时缩容
2.2.2 插入操作:Insert (int i, E e)
 

public void Insert(int i, E e) {

if (i<0 || i>size) { // 检查位置合法性

throw new IllegalArgumentException("插入位置非法");

}

if (size == capacity) { // 空间满时扩容

updatecapacity(2 * size);

}

for (int j=size; j>i; j--) { // 元素后移

data[j] = data[j-1];

}

data[i] = e; // 插入元素

size++; // 表长+1

}

  • 时间复杂度:平均 O (n),最坏情况(i=0)移动 n 次元素
2.2.3 删除操作:Delete (int i)
 

public void Delete(int i) {

if (i<0 || i>=size) { // 检查位置合法性

throw new IllegalArgumentException("删除位置非法");

}

for (int j=i; j<size-1; j++) { // 元素前移

data[j] = data[j+1];

}

size--; // 表长-1

// 缩容条件:容量>初始容量且长度≤容量/4

if (capacity > initcapacity && size == capacity/4) {

updatecapacity(capacity/2);

}

}

  • 时间复杂度:平均 O (n),最坏情况(i=0)移动 n-1 次元素
2.2.4 其他基本运算
  • 末尾添加元素:Add(E e)
 

public void Add(E e) {

if (size == capacity) {

updatecapacity(2 * size); // 扩容

}

data[size++] = e; // 直接添加到末尾

}

  • 获取元素:GetElem(int i)
 

public E GetElem(int i) {

if (i<0 || i>=size) {

throw new IllegalArgumentException("位置非法");

}

return data[i];

}

三、顺序表的典型应用算法

3.1 元素逆置算法

 

public static void Reverse(SqListClass<Integer> L) {

int i = 0, j = L.size() - 1; // 双指针指向首尾

while (i < j) {

L.swap(i, j); // 交换元素

i++; j--; // 指针移动

}

}

  • 核心思想:双指针从两端向中间移动,交换对称位置元素
  • 时间复杂度:O (n),空间复杂度:O (1)

3.2 删除所有值为 x 的元素(三种解法)

解法 3:区间划分法(最高效)
 

public static void Deletex3(SqListClass<Integer> L, Integer x) {

int i = -1, j = 0; // i指向非x区间末尾,j遍历全表

while (j < L.size()) {

if (!L.GetElem(j).equals(x)) { // 找到非x元素

i++; // 扩展非x区间

if (i != j) {

L.swap(i, j); // 交换到正确位置

}

}

j++;

}

L.Setsize(i + 1); // 重置表长

}

  • 核心思想:将表划分为 "非 x 元素区间" 和 "x 元素区间",通过交换实现删除
  • 时间复杂度:O (n),仅一次遍历

3.3 两个有序表合并(二路归并)

 

public static SqListClass<Integer> Merge2(SqListClass<Integer> A, SqListClass<Integer> B) {

SqListClass<Integer> C = new SqListClass<>();

int i = 0, j = 0; // i,j分别遍历A和B

while (i < A.size() && j < B.size()) {

if (A.GetElem(i) < B.GetElem(j)) {

C.Add(A.GetElem(i++)); // 取A中较小元素

} else {

C.Add(B.GetElem(j++)); // 取B中较小元素

}

}

// 处理剩余元素

while (i < A.size()) C.Add(A.GetElem(i++));

while (j < B.size()) C.Add(B.GetElem(j++));

return C;

}

  • 时间复杂度:O (n+m),每个元素仅访问一次
  • 比较次数:最好情况 MIN (n,m),最坏情况 n+m-1

3.4 考研真题:求两个等长有序序列的中位数

 

public static int findMedian(int[] A, int[] B, int n) {

int i = 0, j = 0, k = 0; // k记录已归并元素个数

while (i < n && j < n) {

k++;

if (A[i] < B[j]) {

if (k == n) return A[i]; // 找到第n个元素(中位数)

i++;

} else {

if (k == n) return B[j];

j++;

}

}

return i < n ? A[i] : B[j]; // 理论上不会执行

}

  • 核心思想:归并时只遍历到第 n 个元素,无需合并整个序列
  • 时间复杂度:O (n),空间复杂度:O (1)

四、ArrayList 容器的应用

4.1 ArrayList 常用方法

  • 构造方法
 

ArrayList<Integer> list1 = new ArrayList<>(); // 初始容量10

ArrayList<Integer> list2 = new ArrayList<>(20); // 指定初始容量

  • 核心操作
 

list.add(5); // 末尾添加元素

list.add(1, 10); // 在位置1插入元素

int val = list.get(2); // 获取位置2的元素

list.set(3, 15); // 修改位置3的元素

list.remove(4); // 删除位置4的元素

boolean contains = list.contains(20); // 判断是否包含元素

4.2 ArrayList 元素排序

4.2.1 基本类型排序
 

ArrayList<Integer> nums = new ArrayList<>(Arrays.asList(5, 3, 8, 1, 2));

Collections.sort(nums); // 升序排序:[1,2,3,5,8]

Collections.sort(nums, Collections.reverseOrder()); // 降序排序:[8,5,3,2,1]

4.2.2 对象排序(三种方式)
 

class Student {

private String name;

private int age;

public Student(String name, int age) {

this.name = name;

this.age = age;

}

public String getName() { return name; }

public int getAge() { return age; }

@Override

public String toString() {

return "[" + name + "," + age + "]";

}

// 方式1:实现Comparable接口(按年龄升序)

@Override

public int compareTo(Student o) {

return this.age - o.age;

}

}

public class SortDemo {

public static void main(String[] args) {

ArrayList<Student> students = new ArrayList<>();

students.add(new Student("Tom", 18));

students.add(new Student("Alice", 20));

students.add(new Student("Bob", 18));

// 方式1:直接排序

Collections.sort(students);

System.out.println("按年龄升序: " + students);

// 方式2:自定义Comparator(按姓名升序)

Collections.sort(students, new Comparator<Student>() {

@Override

public int compare(Student s1, Student s2) {

return s1.getName().compareTo(s2.getName());

}

});

System.out.println("按姓名升序: " + students);

// 方式3:Java8 lambda(按姓名降序)

students.sort(Comparator.comparing(Student::getName).reversed());

System.out.println("按姓名降序: " + students);

}

}

五、扩展算法与期末考点

5.1 删除值在 [x,y] 之间的元素

 

public static void deleteRange(SqListClass<Integer> L, int x, int y) {

int i = -1, j = 0; // i指向非区间元素区,j遍历全表

while (j < L.size()) {

if (!(L.GetElem(j) >= x && L.GetElem(j) <= y)) { // 不在区间内的元素

i++;

if (i != j) {

L.swap(i, j); // 交换到正确位置

}

}

j++;

}

L.Setsize(i + 1); // 重置表长

}

  • 时间复杂度:O (n),空间复杂度:O (1)

5.2 有序表删除重复元素

 

public static void deleteDuplicates(SqListClass<Integer> L) {

if (L.size() <= 1) return; // 表长小于2时无需处理

int k = 1; // k记录非重复元素个数

for (int i = 1; i < L.size(); i++) {

if (!L.GetElem(i).equals(L.GetElem(k-1))) {

L.SetElem(k++, L.GetElem(i)); // 非重复元素前移

}

}

L.Setsize(k); // 重置表长

}

  • 前提条件:表已按升序排列
  • 时间复杂度:O(n)

5.3 期末高频考点总结

  1. 顺序表操作的时间复杂度
  • 插入 / 删除:平均 O (n),均摊后插入为 O (1)(考虑扩容)
  • 查找 / 修改:O (1)
  1. 核心算法思想
  • 双指针技术(逆置、归并)
  • 区间划分法(删除指定元素)
  • 二路归并(有序表合并)
  1. ArrayList 排序要点
  • Comparable接口用于类自身定义排序规则
  • Comparator用于外部定制排序逻辑
  • 多字段排序使用thenComparing链式调用

算法复杂度对比表

操作

顺序表时间复杂度

说明

初始化

O(1)

分配初始数组

末尾添加元素

均摊 O (1)

最坏 O (n)(扩容时)

任意位置插入 / 删除

O(n)

元素移动导致

按序号访问元素

O(1)

直接数组索引

按值查找元素

O(n)

顺序遍历比较

六、线性表典型例题

例题 1:顺序表元素逆置

问题描述

设计算法将顺序表 L 中的元素逆置。例如 L=(1,2,3,4,5),逆置后为 (5,4,3,2,1)。

代码实现
 

public static void reverse(SqListClass<Integer> L) {

int i = 0, j = L.size() - 1; // 双指针指向首尾元素

while (i < j) {

L.swap(i, j); // 交换i和j位置的元素

i++; j--; // 指针向中间移动

}

}

例题 2:删除顺序表中所有值为 x 的元素

问题描述

设计算法删除顺序表 L 中所有值为 x 的元素。例如 L=(1,2,1,5,1),x=1 时,删除后为 (2,5)。

代码实现(区间划分法)
 

public static void deleteAllX(SqListClass<Integer> L, Integer x) {

int i = -1, j = 0; // i指向非x区间末尾,j遍历全表

while (j < L.size()) {

if (!L.GetElem(j).equals(x)) { // 找到非x元素

i++;

if (i != j) {

L.swap(i, j); // 交换到正确位置

}

}

j++;

}

L.Setsize(i + 1); // 重置表长

}

例题 3:两个有序顺序表合并

问题描述

合并升序表 A=(1,3,5,8) 和 B=(2,3,8,10,11) 为升序表 C。

代码实现

public static SqListClass<Integer> mergeSortedLists(SqListClass<Integer> A, SqListClass<Integer> B) {

SqListClass<Integer> C = new SqListClass<>();

int i = 0, j = 0;

while (i < A.size() && j < B.size()) {

if (A.GetElem(i) < B.GetElem(j)) {

C.Add(A.GetElem(i++));

} else {

C.Add(B.GetElem(j++));

}

}

while (i < A.size()) C.Add(A.GetElem(i++));

while (j < B.size()) C.Add(B.GetElem(j++));

return C;

}

例题 4:考研真题 — 求两个有序序列的中位数

问题描述

两个等长升序序列 A 和 B,求合并后序列的中位数。例如 A=(11,13,15,17,19),B=(2,4,6,8,20),中位数为 11。

代码实现
 

public static int findMedian(int[] A, int[] B, int n) {

int i = 0, j = 0, k = 0;

while (i < n && j < n) {

k++;

if (A[i] < B[j]) {

if (k == n) return A[i];

i++;

} else {

if (k == n) return B[j];

j++;

}

}

return i < n ? A[i] : B[j];

}

例题 5:ArrayList 多字段排序

问题描述

对学生类按年龄升序、姓名降序排序。

代码实现
 

students.sort(Comparator.comparing(Student::getAge)

.thenComparing(Comparator.comparing(Student::getName).reversed()));

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词