数据结构-线性表
一、线性表概述
1.1 线性表介绍
1.1.1 线性表定义
- 线性表是n(n>=0)个数据元素的有限序列
1.1.2 线性表特点
- 在数据元素的非空有限集中:
- 在唯一的一个被称作第一个的数据元素
- 存在唯一的一个被称作最后一个的数据元素
- 除第一个元素之外、集合中的每个元素均只有一个前驱
- 除最后一个元素之外、集合中的每个元素均只有一个后继
二、线性表的实现方式
2.1 顺序表
2.1.1 顺序存储定义
- 用一段地址连续的存储单元依次存储线性表的数据元素
2.1.2 顺序表存储方式
- 可以使用一维数据来实现顺序存储结构,线性表相邻的元素存放在数组相邻的位置
- 根据数组长度是否可变可分为:
- 定长线性表
- 变长线性表
- 根据数组长度是否可变可分为:
2.1.3 顺序表特性
- 逻辑上相邻的数据,在物理存储的位置上也是相邻的
- 可以实现随机访问,存取访问时间复杂度为 O ( 1 ) O(1) O(1)
- 存储密度高,需要预先分配空间
- 不便于插入和删除。当顺序表较大时,插入和删除都会引起大量数据的移动(时间复杂度为 O ( n ) O(n) O(n))。
- 由于难以确定表的长度,所以定义数组大小时可能会需要频繁扩容或有空间大量冗余
2.2 链表
2.2.1 链表定义
- 链表是一种物理上非连续、非顺序的存储结构
2.2.2 结点
(1)结点定义
- 为了表示每个数据元素与它的后继数据元素的逻辑关系,对于数据元素a来说,除了存储其本身的信息之外(数据域),还需要存储指示其直接后继的存储位置(指针域)。
- 这两部分信息组成数据元素a的存储映像,称为结点(Node)
(2) 结点分类
- 后继节点(NextNode)
- 结点后一个结点称为该结点后继节点
- 前继结点(PreNode)
- 结点前一个结点称为该结点前继节点
2.2.3 链表特性
- 对于一般链表包含以下特点
- 链表相对于顺序表每个结点需要额外位置保存其它结点位置信息,占用空间更多
- 访问链表元素时,需要从链表头指针或尾指针(双向链表)链表遍历查找,时间复杂度为 O ( n ) O(n) O(n)
- 链表在物理存储上不连续,因此可以对空间利用率上更高
- 对于插入删除操作只需该表相应位置前后继结点的指向,时间复杂度为 O ( 1 ) O(1) O(1)
2.2.4 链表分类
(1)单向链表
介绍
- 最简单的链表结构,每个结点包含一个数据域与指向后继结点的指针(引用),尾结点的指针(引用)指向空结点
- 一般对于完整单向链表结构包含:
- 头结点
- 其它辅助数据
特点
- 只能实现从头遍历访问,效率不高
(2)双向链表
介绍
- 相对于单链表而言,每个结点还包含一个指向该结点前继结点的指针(引用)
- 一般对于完整双向链表结构包含:
- 头结点
- 尾结点
- 其它辅助数据
特点
- 不仅可以从前往后进行遍历访问,还可以从某个结点往前遍历访问,从任意结点都可以访问到其他节点
- 对于某些操作可以先判断要操作的结点位置更加靠近尾部还是头部,以便决定从尾部遍历还是从头部遍历,时间耗费可接近为单链表的一半
(3)循环链表
介绍
- 相对于单链表而言,尾结点指针(引用)指向头结点
- 一般对于完整循环链表结构包含:
- 头结点
- 其它辅助数据
特点
- 从任意结点都可以访问到其他节点,相对于双向链表,访问该结点之前的结点可能需要循环一圈,效率较低
(4)以上链表的组合或其他
略
三、线性表的实现
3.1 线性表操作接口
3.1.1 线性表接口java实现
package com.niss.list;
/**
* @author Ni187
*/
public interface List<T> {
/**
* 判断是否为空
* @return 是否为空
*/
boolean isEmpty();
/**
* 线性表元素个数
* @return 线性表元素个数
*/
int size();
/**
* 清空链表
* @return 操作是否成功
*/
boolean clear();
/**
* 从前往后获得参数元素位置
* @param data 元素
* @return 参数元素位置
*/
int indexOf(T data);
/**
* 从后往前获得第一个参数元素位置
* @param data 元素
* @return 参数元素位置
*/
int lastIndexOf(T data);
/**
* 在index位置插入元素
* @param index 插入位置
* @param data 插入元素
* @return 是否插入成功
*/
boolean insert(int index,T data);
/**
* 向线性表末尾添加元素
* @param data 元素
* @return 操作是否成功
*/
boolean add(T data);
/**
* 删除线性表中的元素
* @param index 被删除元素位置
* @return 是否删除成功
*/
boolean remove(int index);
/**
* 设置第index个元素为data
* @param index 要置换的元素位置
* @param data 要置换的对象
* @return 原先的元素
*/
T set(int index,T data);
/**
* 获得元素
* @param index 元素位置
* @return 第index个元素
*/
T get(int index);
}
3.2 顺序表实现
3.2.1 顺序表操作部分操作
(1) 插入
- 插入
- 插入先扩容
(2) 删除
- 删除
3.2.2 顺序表Java实现
package com.niss.list;
import java.util.Arrays;
/**
* @author Ni187
*/
public class ArrayList<E> implements List<E> {
/**
* 初始化大小
*/
private final static int INIT_SIZE = 10;
/**
* 当前已存储元素长度
*/
private int size;
Object[] objects;
public ArrayList() {
this(INIT_SIZE);
}
public ArrayList(int size) {
if (size < 0) {
throw new IndexOutOfBoundsException("初始化大小不能小于零!");
}
this.objects = new Object[size];
this.size = 0;
}
/**
* 由数组创建一个线性表
* @param objects 数组
* @return 线性表
*/
public static ArrayList newArrayList(Object[] objects){
ArrayList arrayList = new ArrayList(0);
arrayList.objects = objects;
arrayList.size = objects.length;
return arrayList;
}
/**
* 扩展数组大小
* @return 操作是否成功
*/
private boolean resize() {
int newSize = objects.length + objects.length<<2;
Object[] newObjs = new Object[this.objects.length + newSize];
System.arraycopy(this.objects, 0, newObjs, 0, this.objects.length);
this.objects = newObjs;
return true;
}
@Override
public boolean isEmpty() {
return this.size == 0;
}
@Override
public int size() {
return this.size;
}
@Override
public boolean clear(){
try {
Arrays.fill(objects, null);
this.size = 0;
}catch (Exception e){
e.printStackTrace();
return false;
}
return true;
}
/**
* 分析:时间复杂度O(n)
*/
@Override
public int indexOf(E data) throws NullPointerException {
if (data == null) {
for (int i = 0; i < this.size; i++) {
if (objects[i] == null) {
return i;
}
}
} else {
for (int i = 0; i < this.size; i++) {
if (objects[i].equals(data)) {
return i;
}
}
}
return -1;
}
@Override
public int lastIndexOf(E data) {
if (data == null) {
for (int i = this.size - 1; i >= 0; i--) {
if (objects[i] == null) {
return i;
}
}
} else {
for (int i = this.size - 1; i >= 0; i--) {
if (objects[i].equals(data)) {
return i;
}
}
}
return -1;
}
/**
* 将传入元素插入到相应位置
* @param index 插入位置
* @param data 元素
* @return 插入结果
*/
@Override
public boolean insert(int index, E data) {
//如果位置不合法
if(index<0||index>this.size){
throw new IndexOutOfBoundsException("位置参数不合法");
}
try {
//如果数组正好全部被占用,重设大小
if (this.size >= objects.length-1) {
this.resize();
}
for (int i = this.size; i > index; i--) {
this.objects[i] = this.objects[i-1];
}
this.objects[index] = data;
this.size++;
}catch (Exception e){
e.printStackTrace();
return false;
}
return true;
}
/**
* 添加元素到线性表尾部
* @param data 插入元素
* @return 操作结果
*/
@Override
public boolean add(E data){
return this.insert(this.size, data);
}
@Override
public boolean remove(int index) {
if(index<0||index>=this.size){
throw new IndexOutOfBoundsException("位置参数不合法");
}
try{
for(int i = index+1;i<this.size-1;i++){
objects[i-1] = objects[i];
}
--this.size;
}catch (Exception e){
e.printStackTrace();
return false;
}
return true;
}
@Override
public E set(int index, E data) {
E oldObj = (E) this.objects[index];
return oldObj;
}
@Override
public E get(int i){
if(i>=this.size||i<0){
throw new IndexOutOfBoundsException("越界");
}
return (E)objects[i];
}
/**
* 弹出最后一个元素
* @return 最后一个元素
*/
public E pop(){
if(this.size>0){
--this.size;
Object data = objects[this.size];
objects[this.size] = null;
return (E)data;
}
return null;
}
@Override
public String toString(){
return Arrays.toString(objects);
}
}
3.3 链表实现
3.3.1 链表部分操作实现
(1) 插入
-
靠队头
-
靠队尾
(2) 访问
- 靠队头
- 靠队尾
(3)删除
- 靠队头
3.3.2 单链表实现
(1)Java实现
package com.niss.list;
/**
* @author Ni187
*/
public class LinkedList<E extends Comparable<E>> implements List<E> {
/**
* 节点类
*
* @param <E>
*/
private static class Node<E>{
E elem;
Node<E> next;
public Node(E elem, Node<E> next) {
this.elem = elem;
this.next = next;
}
public Node() {
this(null, null);
}
public static<E> void swapElem(Node<E> n1,Node<E> n2){
E temp = n1.elem;
n1.elem = n2.elem;
n2.elem = temp;
}
}
/**
* 头节点
*/
private Node<E> head;
/**
* 单链表长度
*/
private int size;
public LinkedList() {
this.head = new Node<E>();
}
@Override
public boolean isEmpty() {
return this.head.next == null;
}
@Override
public int size() {
Node<E> currentNode = this.head;
int count = 0;
while (currentNode.next != null) {
currentNode = currentNode.next;
count++;
}
return count;
}
@Override
public boolean clear(){
Node<E> current = head.next;
head.next = null;
while(current!=null){
Node<E> next = current.next;
current.elem = null;
current.next = null;
current = next;
}
return true;
}
@Override
public int indexOf(E data) {
int index = 0;
Node<E> currentNode = this.head;
while(currentNode.next!=null){
currentNode = currentNode.next;
if(currentNode.elem.equals(data)){
return index;
}
index++;
}
return -1;
}
@Override
public int lastIndexOf(E data) {
return this.size-this.indexOf(data);
}
@Override
public boolean insert(int index, E data) {
checkInsertIndex(index);
Node<E> currentNode = this.head;
for(int i = 0;i < index;i++){
currentNode = currentNode.next;
}
currentNode.next = new Node<E>(data,currentNode.next);
this.size++;
return true;
}
@Override
public boolean add(E data) {
return this.insert(this.size, data);
}
@Override
public boolean remove(int index) {
checkQueryIndex(index);
Node<E> currentNode = this.head;
for(int i = 0;i < index;i++){
currentNode = currentNode.next;
}
currentNode.next = currentNode.next.next;
return true;
}
@Override
public E set(int index, E data) {
checkQueryIndex(index);
Node<E> currentCode = this.head;
for (int i = 0; i < index+1; i++) {
currentCode = currentCode.next;
}
E original = currentCode.elem;
currentCode.elem = data;
return original;
}
@Override
public E get(int index) {
checkQueryIndex(index);
Node<E> currentNode = this.head;
for(int i = 0;i <= index;i++){
currentNode = currentNode.next;
}
return currentNode.elem;
}
/**
* 检查查询位置是否合法
* @param index 查询位置
*/
private void checkQueryIndex(int index){
if(index<0||index>=this.size){
throw new RuntimeException("查找位置越界!");
}
}
/**
* 检查插入位置是否合法
* @param index 插入位置
*/
private void checkInsertIndex(int index){
if(index<0||index>this.size){
throw new RuntimeException("插入位置越界!");
}
}
/**
* 获得新的反转链表(浅拷贝)
* @return 新的反转链表
*/
public LinkedList<E> getReversedList(){
LinkedList<E> linkedList = new LinkedList<E>();
if(head.next==null){
return linkedList;
}
//当前节点
Node<E> current = head.next;
//上一个节点
Node<E> preNode = null;
while(current!=null){
//复制一个新节点,值为当前节点的值
Node<E> newNode = new Node<E>(current.elem,current.next);
//新节点的后继节点为上一节点
newNode.next = preNode;
//上一节点设为当前节点
preNode = newNode;
//后移
current = current.next;
}
linkedList.head.next = preNode;
linkedList.size = this.size;
return linkedList;
}
/**
* 反转当前单链表
*/
public void reverse(){
if(head.next==null){
return ;
}
//当前节点
Node<E> current = head.next;
//上一个节点
Node<E> preNode = null;
while(current!=null){
//新节点的后继节点为上一节点
Node<E> next = current.next;
current.next = preNode;
//上一节点设为当前节点
preNode = current;
//后移
current = next;
}
head.next = preNode;
}
/**
* 反转链表-递归算法
* @param node 当前链表头结点
*/
private void reverse0(Node<E> node){
//如果已经到最后一个节点或者第一个节点就为空,头节点后继节点指向该节点返回
if(node == null||node.next==null){
this.head.next = node;
return ;
}
//临时当前节点后继节点
Node<E> next = node.next;
//处理后继节点的倒置
reverse0(next);
//后继节点的后继节点指向该节点
next.next = node;
//本节点的后继节点设为null,保证最后一个节点为null
node.next = null;
}
public void reverse0(){
reverse0(this.head.next);
}
/**
* 合并有序链表
* @param l1 有序单链表l1
* @param l2 有序单链表l2
* @param <E> 实现Comparable接口,可以比较的类
* @return 合并后的有序链表
*/
public static<E extends Comparable<E>> LinkedList<E> mergeList(LinkedList<E> l1,LinkedList<E> l2){
Node<E> head = l1.head;
Node<E> current = l1.head;
Node<E> c1 = l1.head.next;
Node<E> c2 = l2.head.next;
if(c1==null){
return l2;
}
if(c2==null){
return l1;
}
while(c1!=null&&c2!=null){
if(c1.elem==null?c2.elem==null:c1.elem.compareTo(c2.elem) < 0){
current.next = c1;
c1 = c1.next;
}else{
current.next = c2;
c2 = c2.next;
}
current = current.next;
}
if(c1==null){
current.next = c2;
}
if(c2==null){
current.next = c1;
}
LinkedList<E> linkedList = new LinkedList<E>();
linkedList.size = l1.size+l2.size;
l1 =l2 = null;
linkedList.head = head;
return linkedList;
}
public static<E extends Comparable<E>> LinkedList<E> recursionMergeList(LinkedList<E> l1,LinkedList<E> l2){
Node<E> node = recursionMergeNode(l1.head.next, l2.head.next);
LinkedList<E> linkedList = new LinkedList<E>();
linkedList.head.next = node;
return linkedList;
}
/**
* 递归合并有序链表-递归算法
* @param node1 有序单链表表头结点的后继结点
* @param node2 有序单链表l2表头结点的后继结点
* @param <E> 实现Comparable接口,可以比较的类
* @return 合并后的有序链表的头结点
*/
private static<E extends Comparable<E>> Node<E>recursionMergeNode(Node<E> node1,Node<E> node2){
if(node1==null){
return node2;
}
if(node2==null){
return node1;
}
if(node1.elem==null?node2.elem==null:node1.elem.compareTo(node2.elem) < 0){
node1.next = recursionMergeNode(node1.next, node2);
return node1;
}else{
node2.next = recursionMergeNode(node1, node2.next);
return node2;
}
}
@Override
public String toString(){
StringBuilder str = new StringBuilder("[");
Node<E> currentNode = this.head;
while(currentNode.next!=null){
currentNode = currentNode.next;
if(currentNode == head.next){
str.append(currentNode.elem);
}else {
str.append(", ").append(currentNode.elem);
}
}
str.append("]");
return str.toString();
}
}