目录
一. 数据结构概述
数据结构(Data Structure)是一门研究数据的组织和管理的学科。往往从外在表现为一组数据的集合或者容器。
概念解释:
元素(Element):被管理的原子数据,元素类型不限。
集合(Collection):存放元素的容器,需要利用一定的数据结构知识对元素进行组织。
遍历(Traversal)/ 迭代(Iterate):在数据结构的语境下,往往表示对一个集合中的所有元素都按 照一定的顺序处理一次。
数据逻辑结构:指反映数据元素之间的逻辑关系的数据结构,其中的逻辑结构是指数据元素之间的前后间关系,而与他们在计算机中的存储位置无关。
集合:数据结构中的元素之间除了“同属一个集合” 的相互关系外,别无其他关系;
树形结构:数据结构中的元素存在一对多的相互关系;
图形结构:数据结构中的元素存在多对多的相互关系。
数据存储结构:数据的逻辑结构在计算机存储空间中的存放形式称为数据的物理结构(也称为存储结构)。一般来说,一种数据结构的逻辑结构根据需要可以表示成多种存储结构,常用的存储结构有顺序存储、链式存储、索引存储和哈希存储等。
数据的顺序存储结构的特点是:借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系;非顺序存储的特点是:借助指示元素存储地址的指针表示数据元素之间的逻辑关系。
二. 线性表—顺序表
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数 据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表 在物理上存储时,通常以数组和链式结构的形式存储。
Java 中的 List 和 ArrayList
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.ListIterator;
/**
* 线性结构
* 特点:
* 1. 元素和元素之间有前后关系
* 2. 元素会有在第几个位置的概念,位置通过下标(index)表示,从 0 开始
* 3. 插入可以根据位置的不同,分为:头插、尾插、按位置插入
* 4. 删除可以根据位置的不同,分为:头删、尾删、按位置删除
* 5. 遍历可以分为从前往后遍历和从后往前遍历
* 6. Java 中,List 是一个接口,并且是 Collection 的子接口
*/
public interface List extends Collection {
/**
* 将 e 尾插到线性表中
* @参数 e 待插入的元素
* @返回值 一定是 true,表示插入成功。线性表是不会出现插入不成功的情况的
*/
boolean add(元素类型 e);
/**
* 将 e 插入到线性表的 index 位置处;要求原来 index 及之后的元素全部向后移动
* index 的可选范围是 0 <= index <= size()
* @参数 index 插入位置(下标)
* @参数 待插入的元素
*/
void add(int index, 元素类型 e);
/**
* 删除 index 位置的元素,并返回该元素;要求 原来 index + 1 及之后元素全部向前移
动
* index 的可选范围是 0 <= index < size()
* @参数 index 待删除位置(下标)
* @返回值 被删除掉的元素
*/
元素类型 remove(int index);
/**
* 删除从前往后遍历时,遇到的第一个相等的(equals)元素
* @参数 待删除元素
* @返回值 true:删除成功; false:没有找到相等的元素
*/
boolean remove(元素类型 e);
/**
* 返回 index 位置的元素
* index 的可选范围是 0 <= index < size()
* @参数 index 获取元素的位置(下标)
* @返回值 获取到的元素
*/
元素类型 get(int index);
/**
* 用新的元素 e 替换 index 位置的元素,并返回 index 位置的原来的元素
* index 的可选范围是 0 <= index < size()
* @参数 index 待替换元素的位置(下标)
* @参数 e 要替换的新元素
* @返回值 index 位置的老元素
*/
元素类型 set(int index, 元素类型 e);
/**
* 通过遍历的方式,判断与元素 e 相等(equals)的元素是否存在于线性表中
* @参数 e 待查找元素
* @返回 true:包含;false:不包含
*/
boolean contains(元素类型 e);
/**
* 按照从前往后遍历的方式,找到第一个与元素 e 相等(equals)的元素的下标
* @param e 待查找元素
* @return >= 0 表示找到并且返回下标;-1 代表没有找到
*/
int indexOf(元素类型 e);
/**
* 按照从后往前遍历的方式,找到第一个与元素 e 相等(equals)的元素的下标
* @param e 待查找元素
* @return >= 0 表示找到并且返回下标;-1 代表没有找到
*/
int lastIndexOf(元素类型 e);
/**
* 清空线性表,也就是,调用 clear() 后,线性表的 size() == 0;isEmpty() ==
true
*/
void clear();
/**
* 返回线性表中已有元素的个数
* @return 返回元数个数
*/
int size();
/**
* 返回线性表是不是一个空的容器
* @return true 为空容器
*/
boolean isEmpty();
import java.util.*;
public abstract class MyArrayList<E> implements List<E> {
private E[] array;
private int size;
public MyArrayList(){
array = (E[])new Object[16];
size = 0;
}
@Override
public int size() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public boolean contains(Object o) {
if(o == null){
//无法使用 equals 判断
for(int i = 0;i < size; i++){
if(array[i] == o){
return true;
}
}
return false;
} else {
for(int i = 0;i < size;i++){
if(o.equals(array[i])){
return true;
}
}
return false;
}
}
@Override
public Iterator<E> iterator() {
return null;
}
@Override
public Object[] toArray() {
return new Object[0];
}
@Override
public <T> T[] toArray(T[] a) {
return null;
}
@Override
public boolean add(E e) {
ensuerCapacity();
array[size++] = e;
return true;
}
private void ensuerCapacity() {
if(size < array.length){
return;
}
array = (E[]) Arrays.copyOf(array,array.length*2);
}
@Override
public boolean remove(Object o) {
if(o == null){
for(int i = 0;i < size;i++){
if(array[i] == o){
System.arraycopy(array,i + 1,array,i,size - i - 1);
array[--size] = null;
return true;
}
}
return false;
} else {
for(int i = 0;i < size;i++){
if(o.equals(array[i])){
System.arraycopy(array,i + 1,array,i,size - i - 1);
array[--size] = null;
return true;
}
}
return false;
}
}
@Override
public boolean addAll(Collection<? extends E> c) {
for(E e : c){
add(e);
}
return true;
}
@Override
public boolean addAll(int index, Collection<? extends E> c) {
for(E e : c){
add(index,e);
}
return true;
}
@Override
public void clear() {
Arrays.fill(array,null);
size = 0;
}
@Override
public E get(int index) {
if(index < 0 || index < size){
throw new ArithmeticException(String.format("index = %d,size = %d",index,size));
}
return array[index];
}
@Override
public E set(int index, E element) {
if(index < 0 || index <= size){
throw new ArithmeticException(String.format("index = %d,size = %d",index,size));
}
E oldValue = array[index];
array[index] = element;
return oldValue;
}
@Override
public void add(int index, E element) {
if(index < 0 || index > size){
throw new ArithmeticException(String.format("index = %d,size = %d",index,size));
}
ensuerCapacity();
System.arraycopy(array,index,array,index + 1,size - index);
}
}
三. 线性表—链表
元素(element): 真实存于线性表中的内容,是我们关心的核心内容。
结点(node): 为了组织链表而引入的一个结构,除了保存我们的元素之外,还会保存指向下一个结点的引 用
当前结点(current / cur): 表示链表中某个结点。
前驱结点(previous / prev): 表示链表中某个结点的前一个结点;头结点没有前驱结点。
后继结点(next): 表示链表中某个结点的后一个结点;尾结点没有后继结点。
链表的定义和创建
public class Node {
int val;
Node next;
public Node(int val) {
this.val = val;
this.next = null;
}
@Override
public String toString() {
return "Node{" + val + "}";
}
}
Node n1 = new Node(1);
Node n2 = new Node(2);
Node n5 = new Node(5);
n1.next = n2;
n2.next = n5;
n5.next = null;
Node head = n1;
链表的遍历
Node cur = head;
while (cur != null) {
cur = cur.next;
}
链表的元素插入和删除
头删
public static void main(String args) {
Node head = build();
head = pushFront(head, 0);
// 通过遍历打印,验证头插是否正确
}
private static Node pushFront(Node head, int v) {
Node node = new Node(v);
node.next = head;
head = node;
return head;
}
头插
public static void main(String args) {
Node head = build(); // 需要考虑以下情况:链表为空;链表中有元素
head = popFront(head);
// 通过遍历打印,验证头删是否正确
}
private static Node popFront(Node head) {
if (head == null) {
throw new RuntimeException("链表为空");
}
head = head.next;
return head;
}
尾删
public static void main(String args) {
Node head = build(); // 需要考虑以下情况:链表为空;链表中有元素
head = pushBack(head, 0);
// 通过遍历打印,验证尾插是否正确
}
private static Node pushBack(Node head, int v) {
if (head == null) {
Node node = new Node(v);
return node;
}
Node last = head;
while (last.next != null) {
last = last.next;
}
Node node = new Node(v);
last.next = node;
return head;
}
尾插
public static void main(String args) {
Node head = build(); // 需要考虑以下情况:链表为空;链表中有一个元素;链表中有
多个元素
head = popFront(head);
// 通过遍历打印,验证头删是否正确
}
private static Node popBack(Node head) {
if (head == null) {
throw new RuntimeException("链表为空");
}
if (head.next == null) {
head.next = null;
return head;
}
Node last2 = head;
while (last2.next.next != null) {
last2 = last2.next;
}
last2.next = null;
return head;
}