HMJAVA数据结构与算法4【线性表】
1、顺序表
1.1 顺序表实现

package com.haifei.demo02linear; import java.util.Iterator; public class SequenceList <T>{ //存储元素的数组 private T[] eles; //记录当前顺序表中的元素个数 private int N; //构造方法 public SequenceList(int capacity){ //初始化数组 this.eles = (T[])new Object[capacity]; this.N = 0; } //将线性表置空 public void clear(){ this.N = 0; } //判断当前线性表是否为空表 public boolean isEmpty(){ return N == 0; } //获取线性表长度 public int length(){ return N; } //获取线性表指定位置的元素 public T get(int i){ return eles[i]; } //向线性表中追加元素t public void insert(T t){ eles[N++] = t; } //在线性表索引i处插入元素t public void insert(int i, T t){ //先把i索引处及其后面的元素依次向后移动一位 for (int index=N-1; index>i; index--){ eles[index] = eles[index-1]; } //再将t元素添加到i索引处 eles[i] = t; } //删除线性表索引i处的元素,并返回该元素 public T remove(int i){ //记录索引i处的元素 T current = eles[i]; //索引i处后面的元素依次向前移动一位 for (int index=i; index<N-1; index++){ eles[index] = eles[index+1]; } //元素个数-1 N--; //返回已删除元素 return current; } //查照线性表中t元素第一次出现的位置 public int indexOf(T t){ for (int i=0; i<N; i++){ if (eles[i].equals(t)){ return i; } } return -1; //线性表当前不存在t元素 } }

package com.haifei.demo02linear.test; import com.haifei.demo02linear.SequenceList; public class Test01SequenceList { public static void main(String[] args) { //创建顺序表对象 SequenceList<String> sl = new SequenceList<>(10); //测试插入 sl.insert("姚明"); sl.insert("科比"); sl.insert("麦迪"); sl.insert(1,"詹姆斯"); //测试获取 String getResult = sl.get(1); System.out.println("获取索引1处的结果为:"+getResult); //詹姆斯 //测试删除 String removeResult = sl.remove(0); System.out.println("删除的元素是:"+removeResult); //姚明 //测试清空 sl.clear(); System.out.println("清空后的线性表中的元素个数为:"+sl.length()); //0 } }
1.2 顺序表遍历

package com.haifei.demo02linear; import java.util.Iterator; public class SequenceList <T> implements Iterable<T>{ //存储元素的数组 private T[] eles; //记录当前顺序表中的元素个数 private int N; //构造方法 public SequenceList(int capacity){ //初始化数组 this.eles = (T[])new Object[capacity]; this.N = 0; } //将线性表置空 public void clear(){ this.N = 0; } //判断当前线性表是否为空表 public boolean isEmpty(){ return N == 0; } //获取线性表长度 public int length(){ return N; } //获取线性表指定位置的元素 public T get(int i){ return eles[i]; } //向线性表中追加元素t public void insert(T t){ eles[N++] = t; } //在线性表索引i处插入元素t public void insert(int i, T t){ //先把i索引处及其后面的元素依次向后移动一位 for (int index=N; index>i; index--){ eles[index] = eles[index-1]; } //再将t元素添加到i索引处 eles[i] = t; //元素个数+1 N++; } //删除线性表索引i处的元素,并返回该元素 public T remove(int i){ //记录索引i处的元素 T current = eles[i]; //索引i处后面的元素依次向前移动一位 for (int index=i; index<N-1; index++){ eles[index] = eles[index+1]; } //元素个数-1 N--; //返回已删除元素 return current; } //查照线性表中t元素第一次出现的位置 public int indexOf(T t){ for (int i=0; i<N; i++){ if (eles[i].equals(t)){ return i; } } return -1; //线性表当前不存在t元素 } @Override public Iterator<T> iterator() { return new SIterator(); } private class SIterator implements Iterator{//内部类 private int cusor; public SIterator(){ this.cusor = 0; } @Override public boolean hasNext() { return cusor<N; } @Override public Object next() { return eles[cusor++]; } } }

package com.haifei.demo02linear.test; import com.haifei.demo02linear.SequenceList; public class Test01SequenceList { public static void main(String[] args) { //创建顺序表对象 SequenceList<String> sl = new SequenceList<>(10); //测试插入 sl.insert("姚明"); sl.insert("科比"); sl.insert("麦迪"); sl.insert(1,"詹姆斯"); //测试遍历 for (String s : sl) { System.out.println(s); } /* 姚明 詹姆斯 科比 麦迪 */ System.out.println("========================"); //测试获取 String getResult = sl.get(1); System.out.println("获取索引1处的结果为:"+getResult); //詹姆斯 //测试删除 String removeResult = sl.remove(0); System.out.println("删除的元素是:"+removeResult); //姚明 //测试清空 sl.clear(); System.out.println("清空后的线性表中的元素个数为:"+sl.length()); //0 } }
1.3 顺序表的容量可变

package com.haifei.demo02linear.test; import com.haifei.demo02linear.SequenceList; public class Test02SequenceList { public static void main(String[] args) { SequenceList<String> sl = new SequenceList<>(3); sl.insert("张三"); sl.insert("李四"); sl.insert("王五"); // sl.insert("赵六"); //java.lang.ArrayIndexOutOfBoundsException: 3 } }

package com.haifei.demo02linear; import java.util.Iterator; public class SequenceList <T> implements Iterable<T>{ //存储元素的数组 private T[] eles; //记录当前顺序表中的元素个数 private int N; //构造方法 public SequenceList(int capacity){ //初始化数组 this.eles = (T[])new Object[capacity]; this.N = 0; } //将线性表置空 public void clear(){ this.N = 0; } //判断当前线性表是否为空表 public boolean isEmpty(){ return N == 0; } //获取线性表长度 public int length(){ return N; } //获取线性表指定位置的元素 public T get(int i){ return eles[i]; } //向线性表中追加元素t /*public void insert(T t){ eles[N++] = t; }*/ public void insert(T t){ if (N == eles.length){ //扩容 resize(2 * eles.length); } eles[N++] = t; } //在线性表索引i处插入元素t /*public void insert(int i, T t){ //先把i索引处及其后面的元素依次向后移动一位 for (int index=N-1; index>i; index--){ eles[index] = eles[index-1]; } //再将t元素添加到i索引处 eles[i] = t; }*/ /*public void insert(int i, T t){ //先把i索引处及其后面的元素依次向后移动一位 for (int index=N; index>i; index--){ eles[index] = eles[index-1]; } //再将t元素添加到i索引处 eles[i] = t; //元素个数+1 N++; }*/ public void insert(int i, T t){ if (N == eles.length){ //扩容 resize(2 * eles.length); } //先把i索引处及其后面的元素依次向后移动一位 for (int index=N; index>i; index--){ eles[index] = eles[index-1]; } //再将t元素添加到i索引处 eles[i] = t; //元素个数+1 N++; } //删除线性表索引i处的元素,并返回该元素 /*public T remove(int i){ //记录索引i处的元素 T current = eles[i]; //索引i处后面的元素依次向前移动一位 for (int index=i; index<N-1; index++){ eles[index] = eles[index+1]; } //元素个数-1 N--; //返回已删除元素 return current; }*/ public T remove(int i){ //记录索引i处的元素 T current = eles[i]; //索引i处后面的元素依次向前移动一位 for (int index=i; index<N-1; index++){ eles[index] = eles[index+1]; } //元素个数-1 N--; if (N < eles.length/4){ //缩容 resize(eles.length / 2); } //返回已删除元素 return current; } //查照线性表中t元素第一次出现的位置 public int indexOf(T t){ for (int i=0; i<N; i++){ if (eles[i].equals(t)){ return i; } } return -1; //线性表当前不存在t元素 } //根据参数newSize,重置eles的大小 public void resize(int newSize){ //定义一个临时数组,指向原数组 T[] temp=eles; //创建新数组 eles=(T[])new Object[newSize]; //把原数组的数据拷贝到新数组 for(int i=0;i<N;i++){ eles[i]=temp[i]; } } @Override public Iterator<T> iterator() { return new SIterator(); } private class SIterator implements Iterator{//内部类 private int cusor; public SIterator(){ this.cusor = 0; } @Override public boolean hasNext() { return cusor<N; } @Override public Object next() { return eles[cusor++]; } } }

package com.haifei.demo02linear.test; import com.haifei.demo02linear.SequenceList; public class Test02SequenceList { public static void main(String[] args) { /*SequenceList<String> sl = new SequenceList<>(3); sl.insert("张三"); sl.insert("李四"); sl.insert("王五"); // sl.insert("赵六"); //java.lang.ArrayIndexOutOfBoundsException: 3*/ //在SequenceList类中添加resize()以及对应插入删除配置后 SequenceList<String> sl = new SequenceList<>(3); sl.insert("张三"); sl.insert("李四"); sl.insert("王五"); sl.insert("赵六"); //ok } }
1.4 顺序表的时间复杂度
1.5 Java中ArrayList实现
2、链表
2.1 单向链表

package cn.itcast.algorithm.linear; import java.util.Iterator; public class LinkList<T> implements Iterable<T>{ //记录头结点 private Node head; //记录链表的长度 private int N; //结点类 private class Node { //存储数据 T item; //下一个结点 Node next; public 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; } //获取指定位置i出的元素 public T get(int i) { //通过循环,从头结点开始往后找,依次找i次,就可以找到对应的元素 Node n = head.next; for(int index=0;index<i;index++){ n=n.next; } 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-1;index++){ 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-1;i++){ pre=pre.next; } //要找到i位置的结点 Node curr = pre.next; //找到i位置的下一个结点 Node nextNode = curr.next; //前一个结点指向下一个结点 pre.next=nextNode; //元素个数-1 N--; return curr.item; } //查找元素t在链表中第一次出现的位置 public int indexOf(T t) { //从头结点开始,依次找到每一个结点,取出item,和t比较,如果相同,就找到了 Node n = head; for(int i=0;n.next!=null;i++){ n=n.next; if (n.item.equals(t)){ return i; } } return -1; } @Override public Iterator<T> iterator() { return new LIterator(); } private class LIterator implements Iterator{ private Node n; public LIterator(){ this.n=head; } @Override public boolean hasNext() { return n.next!=null; } @Override public Object next() { n = n.next; return n.item; } } //用来反转整个链表 public void reverse(){ //判断当前链表是否为空链表,如果是空链表,则结束运行,如果不是,则调用重载的reverse方法完成反转 if (isEmpty()){ return; } reverse(head.next); } //反转指定的结点curr,并把反转后的结点返回 public Node reverse(Node curr){ if (curr.next==null){ head.next=curr; return curr; } //递归的反转当前结点curr的下一个结点;返回值就是链表反转后,当前结点的上一个结点 Node pre = reverse(curr.next); //让返回的结点的下一个结点变为当前结点curr; pre.next=curr; //把当前结点的下一个结点变为null curr.next=null; return curr; } }
2.2 双向链表

package cn.itcast.algorithm.linear; import java.util.Iterator; public class TowWayLinkList<T> implements Iterable<T> { //首结点 private Node head; //最后一个结点 private Node last; //链表的长度 private int N; //结点类 private class Node{ public Node(T item, Node pre, Node next) { this.item = item; this.pre = pre; this.next = next; } //存储数据 public T item; //指向上一个结点 public Node pre; //指向下一个结点 public Node next; } public TowWayLinkList() { //初始化头结点和尾结点 this.head = new Node(null,null,null); this.last=null; //初始化元素个数 this.N=0; } //清空链表 public void clear(){ this.head.next=null; this.head.pre=null; this.head.item=null; this.last=null; this.N=0; } //获取链表长度 public int length(){ return N; } //判断链表是否为空 public boolean isEmpty(){ return N==0; } //获取第一个元素 public T getFirst(){ if (isEmpty()){ return null; } return head.next.item; } //获取最后一个元素 public T getLast(){ if (isEmpty()){ return null; } return last.item; } //插入元素t public void insert(T t){ if (isEmpty()){ //如果链表为空: //创建新的结点 Node newNode = new Node(t,head, null); //让新结点称为尾结点 last=newNode; //让头结点指向尾结点 head.next=last; }else { //如果链表不为空 Node oldLast = last; //创建新的结点 Node newNode = new Node(t, oldLast, null); //让当前的尾结点指向新结点 oldLast.next=newNode; //让新结点称为尾结点 last = newNode; } //元素个数+1 N++; } //向指定位置i处插入元素t public void insert(int i,T t){ //找到i位置的前一个结点 Node pre = head; for(int index=0;index<i;index++){ pre=pre.next; } //找到i位置的结点 Node curr = pre.next; //创建新结点 Node newNode = new Node(t, pre, curr); //让i位置的前一个结点的下一个结点变为新结点 pre.next=newNode; //让i位置的前一个结点变为新结点 curr.pre=newNode; //元素个数+1 N++; } //获取指定位置i处的元素 public T get(int i){ Node n = head.next; for(int index=0;index<i;index++){ n=n.next; } return n.item; } //找到元素t在链表中第一次出现的位置 public int indexOf(T t){ Node n = head; for(int i=0;n.next!=null;i++){ n=n.next; if (n.next.equals(t)){ return i; } } return -1; } //删除位置i处的元素,并返回该元素 public T remove(int i){ //找到i位置的前一个结点 Node pre = head; for(int index=0;index<i;index++){ pre=pre.next; } //找到i位置的结点 Node curr = pre.next; //找到i位置的下一个结点 Node nextNode= curr.next; //让i位置的前一个结点的下一个结点变为i位置的下一个结点 pre.next=nextNode; //让i位置的下一个结点的上一个结点变为i位置的前一个结点 nextNode.pre=pre; //元素的个数-1 N--; return curr.item; } @Override public Iterator<T> iterator() { return new TIterator(); } private class TIterator implements Iterator{ private Node n; public TIterator(){ this.n=head; } @Override public boolean hasNext() { return n.next!=null; } @Override public Object next() { n=n.next; return n.item; } } }
2.3 链表的时间复杂度
2.4 链表翻转
public void reverse(){ if (N==0){ //当前是空链表,不需要反转 return; } reverse(head.next); }
/** * * @param curr 当前遍历的结点 * @return 反转后当前结点上一个结点 */ public Node reverse(Node curr){ //已经到了最后一个元素 if (curr.next==null){ //反转后,头结点应该指向原链表中的最后一个元素 head.next=curr; return curr; } //当前结点的上一个结点 Node pre = reverse(curr.next); pre.next = curr; //当前结点的下一个结点设为null curr.next=null; //返回当前结点 return curr; }
2.5 快慢指针的应用(中间值、单链表有环否、有环链表入口)
快慢指针指的是定义两个指针,这两个指针的移动速度一块一慢,以此来制造出自己想要的差值,
这个差值可以让我们找到链表上相应的结点。一般情况下,快指针的移动步长为慢指针的两倍
/** * 判断链表中是否有环 * @param first 链表首结点 * @return ture为有环,false为无环 */ public static boolean isCircle(Node<String> first) { Node<String> slow = first; Node<String> fast = first; while(fast!=null && fast.next!=null){ fast = fast.next.next; slow = slow.next; if (fast.equals(slow)){ return true; } } return false; }
2.6 循环链表
2.7 约瑟夫问题
3、栈
3.1 栈概述
3.2 栈实现

package cn.itcast.algorithm.linear; import java.util.Iterator; 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; } //判断当前栈中元素个数是否为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; //元素个数+1; N++; } //弹出栈顶元素 public T pop(){ //找到首结点指向的第一个结点 Node oldFirst = head.next; if (oldFirst==null){ return null; } //让首结点指向原来第一个结点的下一个结点 head.next=oldFirst.next; //元素个数-1; 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; } } }
3.3 栈案例

package cn.itcast.algorithm.test; import cn.itcast.algorithm.linear.Stack; public class BracketsMatchTest { public static void main(String[] args) { String str = "上海(长安)())"; boolean match = isMatch(str); System.out.println(str+"中的括号是否匹配:"+match); } /** * 判断str中的括号是否匹配 * @param str 括号组成的字符串 * @return 如果匹配,返回true,如果不匹配,返回false */ public static boolean isMatch(String str){ //1.创建栈对象,用来存储左括号 Stack<String> chars = new Stack<>(); //2.从左往右遍历字符串 for (int i = 0; i < str.length(); i++) { String currChar = str.charAt(i)+ ""; //3.判断当前字符是否为左括号,如果是,则把字符放入到栈中 if (currChar.equals("(")){ chars.push(currChar); }else if(currChar.equals(")")){ //4.继续判断当前字符是否是有括号,如果是,则从栈中弹出一个左括号,并判断弹出的结果是否为null,如果为null证明没有匹配的左括号,如果不为null,则证明有匹配的左括号 String pop = chars.pop(); if (pop==null){ return false; } } } //5.判断栈中还有没有剩余的左括号,如果有,则证明括号不匹配 if (chars.size()==0){ return true; }else{ return false; } } }

package cn.itcast.algorithm.test; import cn.itcast.algorithm.linear.Stack; public class ReversePolishNotationTest { public static void main(String[] args) { //中缀表达式 3*(17-15)+18/6 的逆波兰表达式如下 6+3=9 String[] notation = {"3", "17", "15", "-", "*", "18", "6", "/", "+"}; int result = caculate(notation); System.out.println("逆波兰表达式的结果为:" + result); } /** * @param notaion 逆波兰表达式的数组表示方式 * @return 逆波兰表达式的计算结果 */ public static int caculate(String[] notaion) { //1.定义一个栈,用来存储操作数 Stack<Integer> oprands = new Stack<>(); //2.从左往右遍历逆波兰表达式,得到每一个元素 for (int i = 0; i < notaion.length; i++) { String curr = notaion[i]; //3.判断当前元素是运算符还是操作数 Integer o1; Integer o2; Integer result; switch (curr) { case "+": //4.运算符,从栈中弹出两个操作数,完成运算,运算完的结果再压入栈中 o1 = oprands.pop(); o2 = oprands.pop(); result = o2 + o1; oprands.push(result); break; case "-": //4.运算符,从栈中弹出两个操作数,完成运算,运算完的结果再压入栈中 o1 = oprands.pop(); o2 = oprands.pop(); result = o2 - o1; oprands.push(result); break; case "*": //4.运算符,从栈中弹出两个操作数,完成运算,运算完的结果再压入栈中 o1 = oprands.pop(); o2 = oprands.pop(); result = o2 * o1; oprands.push(result); break; case "/": //4.运算符,从栈中弹出两个操作数,完成运算,运算完的结果再压入栈中 o1 = oprands.pop(); o2 = oprands.pop(); result = o2 / o1; oprands.push(result); break; default: //5.操作数,把该操作数放入到栈中; oprands.push(Integer.parseInt(curr)); break; } } //6.得到栈中最后一个元素,就是逆波兰表达式的结果 int result = oprands.pop(); return result; } }
4、队列

package cn.itcast.algorithm.linear; import java.util.Iterator; 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; } //向队列中插入元素t public void enqueue(T t){ if (last==null){ //当前尾结点last为null last= new Node(t,null); head.next=last; }else { //当前尾结点last不为null Node oldLast = last; last = new Node(t, null); oldLast.next=last; } //元素个数+1 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; } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!