根据B站视频整理:https://www.bilibili.com/video/BV1iJ411E7xW?p=39
线性表的基本概念
线性表是最基本、最简单、也是最常用的一种数据结构,一个线性表是n个具有相同特性的数据元素的有限序列。
线性表的特征:
- 第一个数据元素没有前驱,这个元素被称为头结点;
- 最后一个元素没有后驱,这个元素被称为尾结点;
- 除了第一个和最后一个,其他数据元素有且仅有一个前驱和后驱。
如果把线性表用数学语言来定义,则可以表示为 (a1, … , ai-1, ai, ai+1, … , an)。
线性表中数据存储的方式可以是顺序存储,也可以是链式存储,按照存储方式不同,可以把线性表分为顺序表和链表。
1、顺序表
顺序表是在计算机内存中以数组的形式保存的线性表,顺序存储是指用一组地址连续的存储单元,依次存储元素,使得线性表中在逻辑结构上相邻的元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反应元素之间逻辑上的相邻关系。
顺序表的API:
成员方法:
public void clear();
//置空链表
public boolean isEmpty();
//判断链表是否为空
public int length();
//获取元素个数
public T get(int i);
//获取线性表中第i个元素的值
public void insert(int i, T t);
//在第i个元素之前插入数据 t
public void insert(T t);
//添加元素t
public T remove(int i);
//删除第i个数据元素
public T indexOf(T t);
//返回线性表中首次出现 t元素 的位置,不存在返回-1
2、单向链表
单向链表是链表的一种,由多个节点组成,每个节点都是由一个数据域和一个指针域组成,数据域用来存数据,指针域用来指向后继节点。有头链表的头结点,数据域不存储数据
代码实现:
public class LinkList<T> {
//记录头结点
private Node head;
//下一个节点
private int N;
//节点类
private class Node{
T item;
Node next;
private Node(T item, Node next){
this.item = item;
this.next = next;
}
}
public LinkList(){
//初始化头结点
this.head = new Node(null,null);
//初始化元素个数
this.N = 0;
};
//清空链表
public void clear() {
head.next = null;
this.N = 0;
}
//获取链表长度
public int length(){
return N;
}
//判断链表是否为空
public boolean isEmpty(){
return N == 0;
}
//获取指定位置的元素 **
public T get(int i){
Node n = head.next;
for (int index = 0; index < i; index++) {
n = n.next;
}
//当程序跑出上边的for循环,返回当前位置元素即可
return n.item;
}
//向链表中添加元素t
public void insert(T t){
//找到最后一个节点
Node n = head;
while (n.next != null){
n = n.next;
}
//创建新节点,保存新元素t
Node newNode = new Node(t, null);
//让最后一个元素指向新节点
n.next = newNode;
//元素个数+1
N++;
}
//向指定位置i,添加元素t
public void insert(int i, T t){
//找到i位置的前一个节点
Node pre = head;
for(int index = 0; index <= i; i++){
pre = pre.next;
}
//找到i位置节点
Node curr = pre.next;
//创建新节点,并将新节点指向i位置节点
Node newNode = new Node(t, curr);
//将原i位置的前一个节点,指向新节点
pre.next = newNode;
//元素个数+1
N++;
}
//删除指定位置i处的元素,返回其值
public T remove(int i){
//找到i位置的前一个节点
Node pre = head;
for(int index = 0; index <= i; i++){
pre = pre.next;
}
//找到i位置的节点
Node curr = pre.next;
//找到i位置的下一个节点
Node nextNode = curr.next;
//前一个节点指向下一个节点
pre.next = nextNode;
//元素个数-1
N--;
return curr.item;
}
//查找元素i在链表第一次出现的位置
public int indexOf(T t){
Node n = head;
for (int i = 0; n.next != null; i++){
n = n.next;
if (n.item.equals(t)){
return i;
}
}
return -1;
}
}
3、栈
栈是一种基于先进后出(FILO)的数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。
栈是一端开口,一段封闭的数据结构,称数据进入到栈的动作为压栈,数据出来动作为弹栈。
成员方法:
public boolean isEmpty()
//判断栈是否为空,是返回true,否返回false
public int size()
//获取栈中元素的个数
public T pop()
//弹出栈顶元素
publid void push(T t)
//向栈中压入元素 t
代码实现:
java中,栈可以用链表或数组实现,这里用链表实现:
public class Stack<T> implements Iterable<T> {
//记录首节点
private Node head;
//记录栈中元素的个数
private int N;
private class Node {
public T item;
public Node next;
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
public Stack() {
this.head = new Node(null, null);
this.N = 0;
}
public boolean isEmpty() {
return N == 0;
}
public int size() {
return N;
}
//把 t 元素压栈,头插法
public void push(T t) {
//找到首节点指向的第一个节点
Node oldFirst = head.next;
//创建新节点
Node newNode = new Node(t, null);
//将首节点指向新节点
head.next = newNode;
//将新节点指向旧的第一个节点
newNode.next = oldFirst;
N++;
}
// 弹出栈顶元素
public T pop() {
//找到首节点指向的第一个节点
Node oldFirst = head.next;
if (oldFirst == null) {
return null;
}
//让首节点指向第一个节点的下一个节点
head.next = oldFirst.next;
N--;
return oldFirst.item;
}
//提供一个外部遍历方式
@Override
public Iterator<T> iterator() {
return new SIterator();
}
private class SIterator implements Iterator {
private Node n;
public SIterator() {
this.n = head;
}
@Override
public boolean hasNext() {
return n.next != null;
}
@Override
public Object next() {
n = n.next;
return n.item;
}
}
}
测试:
public class StackTest {
public static void main(String[] args) {
//创建栈对象
Stack<String> stack = new Stack<>();
//压栈
stack.push("a");
stack.push("b");
stack.push("c");
stack.push("d");
//遍历输出
for (String item : stack) {
System.out.println(item);
}
System.out.println("----------------");
String result = stack.pop();
System.out.println("弹出的是:"+result);
}
}
4、队列
队列是一种基于先进先出(FIFO)的数据结构,是一种在一端插入,另外一端删除的特殊线性表
队列的API:
成员方法:
public boolean isEmpty()
// 判断是否为空,是返回true,否返回false
public int size()
//获取元素个数
public T dequeue()
//从队列中取出元素
public void enqueue(T t)
//往队列中插入元素
代码实现:
public class Queue<T> implements Iterable<T> {
private Node head;
private Node last;
private int N;
private class Node{
public T item;
public Node next;
public Node(T item, Node next){
this.item = item;
this.next = next;
}
}
public Queue(){
this.head = new Node(null,null);
this.last = null;
this.N = 0;
}
public boolean isEmpty(){
return N == 0;
}
public int size(){
return N;
}
//向队列中插入元素
public void enqueue(T t){
//当前尾结点last为null
if (last == null){
last = new Node(t,null);
head.next = last;
}else {
Node oldLast = last;
last = new Node(t,null);
oldLast.next = last;
}
N++;
}
//从队列中取出元素
public T dequeue(){
if (isEmpty())
return null;
Node oldFirst = head.next;
head.next = oldFirst.next;
N--;
//因为出队列是在删除元素,如果队列删空,需前置last=null
if (isEmpty())
last = null;
return oldFirst.item;
}
@Override
public Iterator<T> iterator() {
return new QIterator();
}
private class QIterator implements Iterator{
private Node n;
public QIterator(){
this.n = head;
}
@Override
public boolean hasNext() {
return n.next != null;
}
@Override
public Object next() {
n = n.next;
return n.item;
}
}
}
测试:
public class QueueTest {
public static void main(String[] args) {
Queue<String> q = new Queue<>();
q.enqueue("a");
q.enqueue("b");
q.enqueue("c");
q.enqueue("d");
for (String str : q) {
System.out.println(str);
}
System.out.println("---------");
String r = q.dequeue();
System.out.println("出队是:" + r + "剩余:" + q.size());
}
}