Java 数据结构_线性表
本文由 Luzhuo 编写,转发请保留该信息.
原文: http://blog.csdn.net/rozol/article/details/78446966
分类
顺序表(一维数组)
- 顺序表:
- 线性表是n个数据元素的有限序列
- 优缺点
- 优点
- 存取速度快(随机存取)
- 缺点
- 增删元素时,造成大量的数据元素移动
- 存储空间是静态分配,定义太大浪费空间,太小不够用,难以估计存储规模时采用链式表为好
- 优点
Code
顺序表实现代码
顺序表实现代码
/**
* ArrayList, 顺序表,一元数组
* @author Luzhuo
*/
public class ArrayList {
private int[] array; // 数组内存指针
private int size; // 数组的容量
private int length; // 已存数据的数量
public ArrayList(){
init(1024);
}
public ArrayList(int size){
init(size);
}
/**
* 创建数组<br>
* 设置数组容量, 创建数组空间, 设置指针偏移量0
* @param size 创建数组的大小
*/
private void init(int size){
this.size = size;
this.array = new int[size]; // 创建数组内存空间
this.length = 0;
}
/**
* 删除数组<br>
* 释放数组内存, 数组容量和指针偏移量将在创建时重新设置
*/
public void destroy(){
this.array = null; // 释放数组内存空间
}
/**
* 清空数据<br>
* 设置指针偏移量0, 添加数据时, 数组中的旧数据将被覆盖
*/
public void clear(){
this.length = 0;
}
/**
* 数组是否为空<br>
* 指针偏移量不为0则表示有数据
* @return true 为空 / false 不为空
*/
public boolean isEmpty(){
// 如果已存数据数量为0,则表示数组为空
return this.length == 0 ? true : false;
}
/**
* List的长度(已存数据的数量)<br>
* 指针偏移量为已存储数据的数量
* @return 返回数组中已存储的数据数
*/
public int length(){
return this.length;
}
/**
* 获取数据<br>
* 通过索引直接获取数组中对应的数据
* @param index 索引
* @return 数据
*/
public int getData(int index){
// 判断传入参数是否合法
if(index < 0 || index >= this.size){
throw new ArrayIndexOutOfBoundsException("索引越界.");
}
return array[index];
}
/**
* 获取指定元素的(第一个)索引<br>
* 遍历寻找指定元素, 找到该元素则返回其对应的索引值
* @param data 数据
* @return 找到指定元素返回元素索引, 否则返回-1
*/
public int getIndex(int data){
for(int i = 0; i < this.length; i++){
if(this.array[i] == data) return i;
}
return -1;
}
/**
* 获取指定元素的直接前驱<br>
* 遍历查找指定的元素, 找到该元素则获取对应的索引值, 数组中该索引值-1位置的元素便是其直接前驱
* @param data 指定元素
* @return 返回指定元素的直接前驱; 没有找到该元素 或者 是第一个元素, 直接返回-1
*/
public int priorData(int data){
// 获取元素的位置
int tempIndex = getIndex(data);
// 没有找到该元素 或者 是第一个元素, 直接返回-1
if(tempIndex == -1 || tempIndex == 0) return -1;
return array[tempIndex - 1];
}
/**
* 获取指定元素的直接后继<br>
* 遍历查找指定的元素, 找到该元素则获取对应的索引值, 数组中该索引值+1位置的元素便是其直接后继
* @param data 指定元素
* @return 返回指定元素的直接后继, 没有找到该元素 或者 是最后一个元素, 直接返回-1
*/
public int nextData(int data){
// 获取元素的位置
int tempIndex = getIndex(data);
// 没有找到该元素 或者 是最后一个元素, 直接返回-1
if(tempIndex == -1 || tempIndex == this.length - 1) return -1;
return array[tempIndex + 1];
}
/**
* 遍历List
*/
public void traverse(){
for(int i = 0; i < this.length; i++){
System.out.println(this.array[i]);
}
}
/**
* 插入数据<br>
* 先将指定位置及其所有后继元素全部向后平移一位(从最末尾开始移), 然后插入到指定位置, 指针偏移量+1
* @param index 插入的位置
* @param data 插入的数据
* @return 插入成功返回true; 索引越界将返回false
*/
public boolean insert(int index, int data){
if(index < 0 || index > this.length){
throw new ArrayIndexOutOfBoundsException("索引越界.");
}
// 将index位置及其所有后继元素全部向后平移一位,从最末尾的元素开始移
for(int i = this.length -1; i >= index; i--){
this.array[i + 1] = this.array[i];
}
// 移完数据后,在插入数据
this.array[index] = data;
this.length++;
return true;
}
/**
* 删除数据<br>
* 将指定位置及其所有后继元素向前平移一位, 指针偏移量-1
* @param index 删除指定的元素
* @return 返回被删除的元素
*/
public int delete(int index){
if(index < 0 || index >= this.length){
throw new ArrayIndexOutOfBoundsException("索引越界.");
}
// 先将要删除的元素拷贝
int temp = this.array[index];
// 拷贝完之后,将index位置与之后所有后继元素向前移动一位
for(int i = index + 1; i < this.length; i++){
this.array[i-1] = this.array[i];
}
this.length--;
return temp;
}
}
顺序表测试代码
public class ArrayListTest {
public static void main(String[] args) {
// ArrayList的测试
ArrayList list = new ArrayList();
for(int x = 0; x < 10; x++){
list.insert(x, x);
}
list.traverse();
list.delete(3);
list.traverse();
System.out.println(list.length());
System.out.println(list.getData(5));
System.out.println(list.getIndex(5));
System.out.println(list.priorData(5));
System.out.println(list.nextData(5));
System.out.println(list.isEmpty());
list.traverse();
list.clear();
System.out.println(list.isEmpty());
list.traverse();
list.destroy();
}
}
链式表
- 静态链表
- 数组形式,插入和删除时无需移动元素,仅修改指针即可
- 最后结点的指针指向0
- 单链表
- 只能沿着一个方向查找,不能反向查找,最后一个结点指针域的值是null
- 单向循环链表
- 将单链表的最后一个结点指针域的null指向了第一个结点
- 以末尾结点指针为已知条件
- 双向链表
- 双向列表保存了直接前趋结点和直接后继结点的指针
- 末尾结点后继指针域为null
- 头指针的前趋指针域为null
- 双向循环链表
- 末尾结点的后继指针域直线给第一个结点
- 头结点的前趋指针域指向最后一个结点
- 优缺点
- 优点
- 增删元素快,不会造成大量的元素移动,但是需要遍历寻找
- 缺点
- 存取速度慢(顺序存取,需要遍历查找)
- 优点
Code
单链表实现代码
单链表实现代码
/**
* LinkedList, 单链表
* @author Luzhuo
*/
public class LinkedList {
public Node node; // 头结点
public int length; // 已存的数据数量
public LinkedList(){
init();
}
/**
* 创建单链表<br>
* 创建头结点, 空数据, 头结点指针=>null, 指针偏移量0
*/
private void init(){
// 初始化头结点
this.node = new Node();
node.data = 0;
node.next = null;
length = 0;
}
/**
* 删除单链表<br>
* 遍历删除所有结点(包括头结点), 头结点指针=>null, 指针偏移量0
*/
public void destroy(){
// 释放所有结点
// 删除头结点的所有后继结点
clear();
// 删除头结点
this.node = null;
}
/**
* 清空数据
* 遍历删除头结点的所有后继结点, 头结点指针=>null, 指针偏移量0
*/
public void clear(){
// 保留头结点,释放所有后继结点
// 通过头结点找到下个结点
Node curN = this.node.next;
// 该结点的指针域不为null
while(curN != null){
Node tempN = curN.next;
// 已通过该结点找到下个结点,该结点无利用价值,将其删除
curN = null;
curN = tempN;
}
// 删除完数据后,将头结点的指针域置为null
this.node.next = null;
this.length = 0;
}
/**
* LinkedList是否为空<br>
* 指针偏移量不为0, 则表示有数据
* @return LinkedList为空返回true,否则返回false
*/
public boolean isEmpty(){
return this.length == 0 ? true : false;
}
/**
* 获取LinkedList的长度(已存储数据的数量)<br>
* 指针偏移量为已存储数据的数量
* @return 数据长度
*/
public int length(){
return this.length;
}
/**
* 插入数据到头部(头结点后)<br>
* 交换指针, 头结点指针=>newNode, newNode=>tempNode, 指针偏移量+1
* @param data 要插入的数据
*/
public boolean insertFrist(Node data){
// 临时保存头结点的指针域里的指针
Node tempN = this.node.next;
// 创建新的结点,并赋值数据
Node newN = new Node();
newN.data = data.data;
// 交换结点,将新结点的指针给头结点, 将临时保存的指针赋值到新节点的指针域
this.node.next = newN;
newN.next = tempN;
this.length++;
return true;
}
/**
* 插入数据到末尾<br>
* 寻找末尾结点, Node=>NewNode, NewNode=>null, 指针偏移量+1
* @param data
* @return
*/
public boolean insertLast(Node data){
// 寻找最后一个结点
Node curN = this.node;
while(curN.next != null){
curN = curN.next;
}
// 创建新的结点, 并将指针交给上边找出来的最后一个结点.
Node newN = new Node();
newN.data = data.data;
newN.next = null;
curN.next = newN;
this.length++;
return true;
}
/**
* 插入数据<br>
* 找到指定位置的结点(IndexNode), 交换指针, NewNode=>AfterNode, IndexNode=>NewNode, 指针偏移量+1
* @param index 插入位置
* @param data 插入的数据
* @return 插入数据是否成功
*/
public boolean insert(int index, Node data){
if(index < 0 || index > this.length){
throw new ArrayIndexOutOfBoundsException("索引越界.");
}
// 找到index位置的结点
Node curN = this.node;
for(int i = 0; i < index; i++){
curN = curN.next;
}
// 创建新的结点并交换指针,先将index结点指针域里的指针交给新结点,再将新结点指针赋值到index结点的指针域
Node newN = new Node();
newN.data = data.data;
newN.next = curN.next;
curN.next = newN;
this.length++;
return true;
}
/**
* 删除元素<br>
* 找到指定位置元素的结点(IndexNode), 交换指针, BeforeNode=>AfterNode, 指针偏移量-1
* @param index
* @return 返回被删除的元素
*/
public Node delete(int index){
if(index < 0 || index >= this.length) {
throw new ArrayIndexOutOfBoundsException("索引越界.");
}
Node curN = this.node;
Node curNBefore = null;
for(int i = 0; i <= index; i++){
curNBefore = curN; // 保存index的直接前趋结点,再获取index结点
curN = curN.next; // 获取index结点
}
// 交换结点;将index结点指针域里的指针交给直接前趋结点
curNBefore.next = curN.next;
Node temp = curN;
temp.next = null;
this.length--;
return temp;
}
/**
* 获取数据<br>
* 遍历获取指定位置的元素
* @param index 位置
* @return
*/
public Node getData(int index){
if(index < 0 || index >= this.length) {
throw new ArrayIndexOutOfBoundsException("索引越界.");
}
Node curN = this.node;
for(int i = 0; i <= index; i++){
curN = curN.next;
}
return curN;
}
/**
* 查找数据的(第一个)索引<br>
* 遍历查找指定元素
* @param data 查找的数据
* @return 找到返回索引,未找到返回-1
*/
public int getIndex(Node data){
Node curN = this.node;
int count = 0;
while(curN.next != null){
curN = curN.next;
if(curN.data == data.data){
return count;
}
count++;
}
return -1;
}
/**
* 查找数据的直接前趋结点的数据<br>
* 遍历查找指定元素(同时缓存上个结点), 返回缓存的上个结点
* @param data
* @return
*/
public int priorData(Node data){
Node curN = this.node;
Node tempN = null;
while(curN.next != null){
tempN = curN;
curN = curN.next;
if(curN.data == data.data){
if(tempN == this.node) return -1; // 第一个元素没有前趋 元素
int priData = tempN.data;
return priData;
}
}
return -1;
}
/**
* 查找数据的直接后继结点的数据<br>
* 遍历查找指定结点, 返回下个结点
* @param data
* @return
*/
public int nextData(Node data){
Node curN = this.node;
while(curN.next != null){
curN = curN.next;
if(curN.data == data.data){
if(curN.next == null) return -1; // 最后一个结点没有后继结点
return curN.next.data;
}
}
return -1;
}
/**
* 遍历<br>
* 根据结点的next指针域里的指针, 依次向下遍历结点
*/
public void traverse(){
Node curN = this.node;
while(curN.next != null){
curN = curN.next;
curN.print();
}
}
}
/**
* 结点
* @author Luzhuo
*/
class Node{
public int data; // 数据域
public Node next; // 指针域(指向下个结点)
public Node(){ }
public Node(int data){
this.data = data;
}
public void print() {
System.out.println(("Next: ".concat(next == null ? "null" : next.toString()).concat("; Data: ").concat(String.valueOf(data))));
}
}
单链表测试代码
public class LinkedListTest {
public static void main(String[] args) {
// 测试
LinkedList list = new LinkedList();
for(int x = 0; x < 10; x++){
list.insert(x, new Node(x));
}
list.traverse();
list.delete(3);
list.insertFrist(new Node(11));
list.insertLast(new Node(99));
list.traverse();
System.out.println(list.isEmpty());
System.out.println(list.length());
System.out.println(list.getData(3).data);
System.out.println(list.getIndex(new Node(5)));
System.out.println(list.priorData(new Node(5)));
System.out.println(list.nextData(new Node(5)));
list.clear();
System.out.println(list.isEmpty());
System.out.println(list.length());
list.traverse();
list.destroy();
}
}
双向循环链表
LinkedHashMap是java中自带双向循环链表数据结构
LinkedHashMap源码分析
特殊的线性表
栈 和 队列 属于线性表中较特殊的一种, 因为 栈 被限制为后进先出, 队列被限制为 先进先出
栈 和 队列 的实现方式可以为顺序表 或者 链式表.
栈
栈属于线性表中特殊表之一, 后进先出
Code
栈的数组实现代码
/**
* 栈
* @author Luzhuo
*/
public class Stack {
private Elem[] buffer; // 栈空间指针
private int size; // 栈容量
private int length; // 栈中元素个数
public Stack(){
init(1024);
}
public Stack(int size){
init(size);
}
/**
* 创建栈<br>
* 创建数组, 偏移量指针0
* @param size
*/
private void init(int size){
this.size = size;
this.buffer = new Elem[size];
this.length = 0;
}
/**
* 删除栈<br>
* 删除数组, 偏移量指针将在创建时重新设置
*/
public void destory(){
this.buffer = null;
}
/**
* 栈是否为空<br>
* 指针偏移量不为0, 则不为空
* @return true 没有数据, false 有数据
*/
public boolean isEmpty(){
return this.length == 0 ? true : false;
}
/**
* 栈是否已满<br>
* 指针偏移量>=数组容量, 则栈已存满
* @return
*/
public boolean isFull(){
return this.length >= this.size ? true : false;
}
/**
* 清空栈<br>
* 指针偏移量0, 设置数据将直接覆盖数组里的旧数据
*/
public void clear(){
this.length = 0;
}
/**
* 获取栈中元素的个数<br>
* 指针偏移量为已存数据量
* @return
*/
public int length(){
return this.length;
}
/**
* 入栈:添加元素到栈顶<br>
* 添加到数组末尾, 指针偏移量+1
* @param elem 元素
* @return
*/
public boolean push(Elem elem){
if(isFull()) return false;
this.buffer[this.length] = elem;
this.length++;
return true;
}
/**
* 出栈:从栈顶移除第一个元素<br>
* 将数组末尾的数据返回(不删除数据), 指针偏移量-1, 添加数据时直接将其覆盖
* @return 有元素返回元素,没有元素返回null
*/
public Elem pop(){
if(isEmpty()) return null;
this.length--;
return this.buffer[this.length];
}
/**
* 遍历(栈顶到栈底)<br>
*/
public void traverse(){
for(int i = this.length - 1; i >= 0; i--){
System.out.print(this.buffer[i] + " ");
}
}
}
/**
* 存放栈中的元素
* @author Luzhuo
*
*/
class Elem{
public char data;
public Elem(){}
public Elem(char data){
this.data = data;
}
@Override
public String toString() {
return String.valueOf(data);
}
}
栈测试代码
public class StackTest {
public static void main(String[] args) {
// 测试
Stack stack = new Stack(10);
System.out.println(stack.isEmpty());
System.out.println(stack.isFull());
System.out.println(stack.length());
for(int i = 0; i < 10; i++){
stack.push(new Elem((char) (65+i)));
}
stack.push(new Elem('B'));
stack.push(new Elem('C'));
System.out.println(stack.isEmpty());
System.out.println(stack.isFull());
System.out.println(stack.pop());
System.out.println(stack.length());
stack.traverse();
stack.clear();
System.out.println(stack.length());
stack.destory();
}
}
队列
队列属于线性表中特殊表之一, 先进先出
普通队列
环形队列: 弥补了普通队列的处理完任务后内存浪费的情况
Code
环形队列的数组实现代码
public class Queue {
private Elem[] queque; // 队列数组
private int length; // 队列元素个数
private int size; // 队列的容量
private int head; // 队头
private int tail; // 队尾
public Queue(){
init(1024);
}
public Queue(int size){
init(size);
}
/**
* 创建队列<br>
* 创建数组, 头指针0, 尾指针0, 指针偏移量0
* @param size
*/
private void init (int size){
this.size = size;
this.queque = new Elem[size];
clear();
}
public void destory(){
this.queque = null;
}
/**
* 清空队列<br>
* 重置 头指针0, 尾指针0, 指针偏移量0
*/
public void clear(){
this.head = 0;
this.tail = 0;
this.length = 0;
}
/**
* 是否为空<br>
* 如果指针偏移量为0 则说明没有数据
* @return
*/
public boolean isEmpty(){
return this.length == 0 ? true : false;
}
/**
* 是否满了<br>
* 如果偏移量 >= 容量, 说明已经满了
* @return
*/
public boolean isFull(){
return this.length >= this.size ? true : false;
}
/**
* 获取长度<br>
* 指针偏移量就是已存数据量
* @return
*/
public int length(){
return this.length;
}
/**
* 插入元素(插入到队列末尾)<br>
* 插入到队尾, 尾指针位置由 this.tail % this.size 计算获得
* @param elem
* @return
*/
public boolean addElem(Elem elem){
if(isFull()) return false;
if(elem == null) throw new NullPointerException();
this.queque[this.tail] = elem;
this.tail++;
this.tail = this.tail % this.size;
this.length++;
return true;
}
/**
* 获取元素<br>
* 获取头指针处的元素, 然后头指针+1, 指针偏移量-1
* @return
*/
public Elem getElem(){
if(isEmpty()) return null;
Elem elem = this.queque[this.head];
this.head++;
this.head = this.head % this.size;
this.length--;
return elem;
}
/**
* 遍历
*/
public void traverse(){
for(int i = this.head; i < this.length + this.head; i++){
System.out.print(this.queque[i%this.size] + " ");
}
}
}
class Elem{
public char data;
public Elem(){}
public Elem(char data){
this.data = data;
}
@Override
public String toString() {
return String.valueOf(this.data);
}
}
环形队列测试代码
public class QueueTest {
public static void main(String[] args) {
// 测试
Queue que = new Queue(3);
System.out.println(que.isEmpty());
System.out.println(que.isFull());
System.out.println(que.length());
for(int i = 0; i < 5; i++){
que.addElem(new Elem((char)(65+i)));
}
que.traverse();
System.out.println();
System.out.println(que.getElem());
que.traverse();
System.out.println();
que.addElem(new Elem('O'));
que.traverse();
System.out.println();
System.out.println(que.isEmpty());
System.out.println(que.isFull());
System.out.println(que.length());
que.clear();
System.out.println(que.length());
que.destory();
}
}