数据结构与算法(一)稀疏数组和队列
线性结构和非线性结构
数据结构包括:线性结构和非线性结构。
线性结构
- 线性结构作为最常用的数据结构,其特点是数据元素之间存在一对一的线性关系
- 线性结构有两种不同的存储结构,即顺序存储结构和链式存储结构。
- 顺序存储的线性表称为顺序表,顺序表中的存储元素是连续的
- 链式存储的线性表称为链表,链表中的存储元素不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息。
- 线性结构常见的有:数组、队列、链表和栈。
非线性结构
非线性结构包括:二维数组,多维数组,广义表,树结构,图结构
一、稀疏数组和队列
1. 稀疏数组
1.1 概念
所谓稀疏数组就是数组中大部分的内容值都未被使用(或都为零),在数组中仅有少部分的空间使用。因此造成内存空间的浪费,为了节省内存空间,并且不影响数组中原有的内容值,我们可以采用一种压缩的方式来表示稀疏数组的内容。
假设有一个7*7的数组,其内容如下:
在此数组中,共有49个空间,但却只使用了3个元素,造成46个元素空间的浪费。以下我们就使用稀疏数组重新来定义这个数组:
其中在稀疏数组中第一部分所记录的是原数组的列数和行数以及元素使用的个数、第二部分所记录的是原数组中元素的位置和内容。经过压缩之后,原来需要声明大小为49的数组,而使用压缩后,只需要声明大小为4*3的数组,仅需12个存储空间。
1.2 Java简单实现
看一下互相转换的Java代码实现:
public class SparseArray {
public static void main(String[] args) {
// 创建一个原始的二维数组
int orgArray[][] = new int[7][7];
orgArray[1][1] = 1;
orgArray[2][3] = 4;
orgArray[4][4] = 5;
int[][] sparseArray = org2SparseArray(orgArray);
sparse2OrgArray(sparseArray);
}
/**
* 将原始二维数组转换为稀疏数组
*
* @param orgArray 原始的二维数组
*/
public static int[][] org2SparseArray(int[][] orgArray) {
// 输出原始的数组
for (int[] temp : orgArray) {
System.out.println(Arrays.toString(temp));
}
int sum = 0;
for (int i = 0; i < orgArray.length; i++) {
// 获取数组的长度
int[] temp = orgArray[i];
for (int j = 0; j < temp.length; j++) {
if (orgArray[i][j] != 0) {
sum++;
}
}
}
// 创建对应的稀疏数组
int sparseArray[][] = new int[sum + 1][3];
// 给稀疏数组赋值
sparseArray[0][0] = orgArray.length;
sparseArray[0][1] = orgArray[0].length;
sparseArray[0][2] = sum;
// 遍历二维数组,将值保存到稀疏数组
int rowNum = 0;
for (int i = 0; i < orgArray.length; i++) {
// 获取数组的长度
int[] temp = orgArray[i];
for (int j = 0; j < temp.length; j++) {
if (orgArray[i][j] != 0) {
rowNum++;
sparseArray[rowNum][0] = i;
sparseArray[rowNum][1] = j;
sparseArray[rowNum][2] = orgArray[i][j];
}
}
}
// 输出稀疏数组
for (int[] temp : sparseArray) {
System.out.println(Arrays.toString(temp));
}
return sparseArray;
}
/**
* 将稀疏数组转换为原始二维数组
*
* @param sparseArray 稀疏数组
*/
public static void sparse2OrgArray(int[][] sparseArray) {
// 根据稀疏数组生成二维数组
int orgArray[][] = new int[sparseArray[0][0]][sparseArray[0][1]];
// 将稀疏数组的数据复制给原始数组
for (int i = 1; i < sparseArray.length; i++) {
int temp[] = sparseArray[i];
orgArray[temp[0]][temp[1]] = temp[2];
}
// 输出原始的数组
for (int[] temp : orgArray) {
System.out.println(Arrays.toString(temp));
}
}
}
2.队列
2.1 概念
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
2.2 Java简单实现
以下为数组格式的队列简单实现:
public class QueueTest {
public static void main(String[] args) {
ArrayQueue<Integer> arrayQueue = new ArrayQueue<Integer>(7);
arrayQueue.enqueue(new Integer(1));
arrayQueue.enqueue(new Integer(2));
System.out.println(arrayQueue);
arrayQueue.dequeue();
System.out.println(arrayQueue);
}
/**
* 队列的简单实现
*
* @param <T> 队列元素
*/
static class ArrayQueue<T> {
// 保存元素的数组
private T queue[];
// 队头
private int head = 0;
// 队尾
private int tail = 0;
// 元素个数
private int size;
public ArrayQueue(int size) {
this.size = size;
queue = (T[]) new Object[size];
}
/**
* 将元素插入队列
*
* @param t 队列元素
* @return 是否插入成功
*/
public boolean enqueue(T t) {
// 表示队列队容量已满,无法在队尾插入元素
if (tail == size) {
// 队尾等于容量大小,并且队头为0,则表示队列已经存满
if (head == 0) {
System.out.println("队列已满,无法插入元素:" + t);
return false;
} else {
System.out.println("队尾已无法插入,进行数据迁移,并插入队列元素");
// 将数据整体向前移动head单位
for (int i = head; i < tail; i++) {
queue[i - head] = queue[i];
}
// 重置队尾
tail -= head;
// 重置队头
head = 0;
}
}
System.out.println("插入队列元素为:" + t);
// 加入队列 队尾+1
queue[tail++] = t;
return true;
}
/**
* 出队操作
*
* @return 本次出队的元素
*/
public T dequeue() {
// 表示队列为空
if (head == tail) {
return null;
}
// 获取队头元素
Object t = queue[head];
// 将队头元素置空
queue[head] = null;
// 队头向后移动一位,避免每次出队时整体移动
head++;
System.out.println("移除队列元素:" + t);
return (T) t;
}
@Override
public String toString() {
return Arrays.toString(queue);
}
}
}
2.3循环队列数组实现
循环队列的实现主要是依靠取模运算的实现,当队尾满了时,可以通过(tail+1)%size来循环获取队列的下一个位置。
简单代码实现如下
public class CycleQueueTest {
public static void main(String[] args) {
CycleQueue<Object> queue = new CycleQueue<>(3);
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
queue.enqueue(9);
queue.dequeue();
queue.enqueue(4);
queue.dequeue();
queue.dequeue();
queue.enqueue(5);
queue.enqueue(6);
queue.dequeue();
queue.dequeue();
queue.dequeue();
queue.dequeue();
}
static class CycleQueue<E> {
// 队列元素数组
private Object queue[];
// 队列头指针
private int head = -1;
// 队列尾指针
private int tail = -1;
// 队列元素个数
private int size = 0;
public CycleQueue(int size) {
queue = new Object[size];
}
/**
* 插入队列元素
*
* @param e 插入的元素
* @return 插入结果
*/
public boolean enqueue(E e) {
if (size == queue.length) {
System.out.println("队列已满");
return false;
}
System.out.println("本次入队为:" + e);
tail = (tail + 1) % queue.length;
queue[tail] = e;
if (size == 0) {
head = tail;
}
size++;
return true;
}
/**
* 取出队列元素
*
* @return 取出的队列元素
*/
public E dequeue() {
if (size == 0) {
System.out.println("队列为空");
return null;
}
E e = (E) queue[head];
System.out.println("本次出队为:" + e);
queue[head] = null;
head = (head + 1) % queue.length;
size--;
return e;
}
@Override
public String toString() {
return Arrays.toString(queue);
}
}
}