最简单的动态数据结构
更深入的理解引用(或者指针)
更深入的理解递归
辅助组成其他数据结构
优点:真正的动态,不需要处理固定容量问题
缺点 :丧失了随机访问的能力
数组:最好用于索引有语义的情况,支持快速查询
链表:不适合用于索引有语义的情况,动态。
内部类Node
public class LinkedList<E>{
private class Node{
public E e;
public Node next;
public Node(E e , Node next){
this.e=e;
this.next=next;
}
public Node(E e){
this(e,null);
}
public Node(){
this(null,null);
}
@Override
public String toString(){
return e.toString();
}
}
}
添加元素
//在链表的index(0-based)位置添加新的元素e
//在链表中不是一个常用的操作,练习用
public void add(int index, E e) {
if (index < 0 || index > size)
throw new IllegalArgumentException("Add failed, IllegalIndex");
Node prev = dummyHead;
for (int i = 0; i < index; i++)
prev = prev.next;
prev.next = new Node(e, prev.next);
size++;
}
//在表头添加新的元素e
public void addFirst(E e) {
/*Node node=new Node(e,null);
node.next=head;
head=node;*/
/*head=new Node(e,head);
size++;*/
add(0, e);
}
//链表末尾添加新的元素
public void addLast(E e) {
add(size,e);
}
获取元素
//获得链表的第index(0-based)个位置的元素
//在链表中不是一个常用的操作,练习用
public E get(int index){
if(index<0||index>size)
throw new IllegalArgumentException("Get Failed, Illegal Index");
Node cur=dummyHead.next;
for (int i = 0; i < index; i++) {
cur=cur.next;
}
return cur.e;
}
//获得链表第一个元素
public E getFirst(){
return get(0);
}
//获得链表最后一个元素
public E getLast(){
return get(size-1);
}
修改元素
//修改链表的第index(0-based)个位置的元素为e
//在链表中不是一个常用的操作,练习用
public void set(int index, E e){
if(index<0||index>size)
throw new IllegalArgumentException("Set Failed, Illegal Index");
Node cur= dummyHead.next;
for (int i = 0; i < index; i++) {
cur=cur.next;
}
cur.e=e;
}
是否包含元素
//查找链表中是否存在元素e
public boolean contains(E e){
Node cur=dummyHead.next;
/*for (int i = 0; i < size-1; i++) {
if(cur.e==e)return true;
cur=cur.next;
}*/
while(cur!=null){
if(cur.e.equals(e))return true;
cur=cur.next;
}
return false;
}
删除元素
//从链表中删除index(0-based)位置的元素,返回删除的元素
//在链表中不是一个常用的操作,练习用
public E remove(int index){
if(index<0||index>size)
throw new IllegalArgumentException("Remove Failed, Illegal Index");
Node prev=dummyHead;
for (int i = 0; i < index; i++) {
prev=prev.next;
}
Node retNode=prev.next;
prev.next=retNode.next;
retNode.next=null;
size--;
return retNode.e;
}
//从链表中删除第一个元素,返回删除的元素
public E removeFirst(){
return remove(0);
}
//从链表中删除最后一个元素,返回删除的元素
public E removeLast(){
return remove(size-1);
}
输出
@Override
public String toString() {
StringBuilder res=new StringBuilder();
Node cur=dummyHead.next;
while(cur!=null){
res.append(cur.e+"->");
cur=cur.next;
}
res.append("NULL");
return res.toString();
}
LinkedList.java 完整代码
public class LinkedList<E> {
private class Node {
public E e;
public Node next;
public Node(E e, Node next) {
this.e = e;
this.next = next;
}
public Node(E e) {
this(e, null);
}
public Node() {
this(null, null);
}
@Override
public String toString() {
return e.toString();
}
}
private Node dummyHead;
private int size;
public LinkedList() {
dummyHead = new Node();
size = 0;
}
//获取链表中元素个数
public int getSize() {
return size;
}
//链表是否为空
public boolean isEmpty() {
return size == 0;
}
//在表头添加新的元素e
public void addFirst(E e) {
/*Node node=new Node(e,null);
node.next=head;
head=node;*/
/*head=new Node(e,head);
size++;*/
add(0, e);
}
//在链表的index(0-based)位置添加新的元素e
//在链表中不是一个常用的操作,练习用
public void add(int index, E e) {
if (index < 0 || index > size)
throw new IllegalArgumentException("Add failed, IllegalIndex");
Node prev = dummyHead;
for (int i = 0; i < index; i++)
prev = prev.next;
prev.next = new Node(e, prev.next);
size++;
}
//链表末尾添加新的元素
public void addLast(E e) {
add(size,e);
}
//获得链表的第index(0-based)个位置的元素
//在链表中不是一个常用的操作,练习用
public E get(int index){
if(index<0||index>size)
throw new IllegalArgumentException("Get Failed, Illegal Index");
Node cur=dummyHead.next;
for (int i = 0; i < index; i++) {
cur=cur.next;
}
return cur.e;
}
//获得链表第一个元素
public E getFirst(){
return get(0);
}
//获得链表最后一个元素
public E getLast(){
return get(size-1);
}
//修改链表的第index(0-based)个位置的元素为e
//在链表中不是一个常用的操作,练习用
public void set(int index, E e){
if(index<0||index>size)
throw new IllegalArgumentException("Set Failed, Illegal Index");
Node cur= dummyHead.next;
for (int i = 0; i < index; i++) {
cur=cur.next;
}
cur.e=e;
}
//查找链表中是否存在元素e
public boolean contains(E e){
Node cur=dummyHead.next;
/*for (int i = 0; i < size-1; i++) {
if(cur.e==e)return true;
cur=cur.next;
}*/
while(cur!=null){
if(cur.e.equals(e))return true;
cur=cur.next;
}
return false;
}
//从链表中删除index(0-based)位置的元素,返回删除的元素
//在链表中不是一个常用的操作,练习用
public E remove(int index){
if(index<0||index>size)
throw new IllegalArgumentException("Remove Failed, Illegal Index");
Node prev=dummyHead;
for (int i = 0; i < index; i++) {
prev=prev.next;
}
Node retNode=prev.next;
prev.next=retNode.next;
retNode.next=null;
size--;
return retNode.e;
}
//从链表中删除第一个元素,返回删除的元素
public E removeFirst(){
return remove(0);
}
//从链表中删除最后一个元素,返回删除的元素
public E removeLast(){
return remove(size-1);
}
@Override
public String toString() {
StringBuilder res=new StringBuilder();
Node cur=dummyHead.next;
while(cur!=null){
res.append(cur.e+"->");
cur=cur.next;
}
res.append("NULL");
return res.toString();
}
}
Main.java 测试用例
public class Main {
public static void main(String[] args) {
LinkedList<Integer> linkedList=new LinkedList<>();
for (int i = 0; i < 5; i++) {
linkedList.addFirst(i);
System.out.println(linkedList);
}
linkedList.addLast(999);
System.out.println(linkedList);
linkedList.add(3,787);
System.out.println(linkedList);
linkedList.remove(1);
System.out.println(linkedList);
linkedList.removeFirst();
System.out.println(linkedList);
}
}
运行结果:
0->NULL
1->0->NULL
2->1->0->NULL
3->2->1->0->NULL
4->3->2->1->0->NULL
4->3->2->1->0->999->NULL
4->3->2->787->1->0->999->NULL
4->2->787->1->0->999->NULL
2->787->1->0->999->NULL
时间复杂度分析
添加操作 | 时间复杂度 |
---|---|
add(index,e) | O(n/2)=O(n) |
addFirst() | O(1) |
addLast() | O(1) |
删除操作 | 时间复杂度 |
---|---|
remove(index,e) | O(n/2)=O(n) |
removeFirst() | O(1) |
removeLast() | O(n) |
修改操作 | 时间复杂度 |
---|---|
set(index,e) | O(n) |
查询操作 | 时间复杂度 |
---|---|
get(index) | O(n) |
contains(e) | O(n) |
Stack接口
public interface Stack<E> {
int getSize();
boolean isEmpty();
void push(E e);
E pop();
E peek();
}
具体实现类 LinkedListStack.java
public class LinkedListStack<E> implements Stack<E> {
private LinkedList<E> list;
public LinkedListStack(){
list=new LinkedList<>();
}
@Override
public int getSize() {
return list.getSize();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public void push(E e) {
list.addFirst(e);
}
@Override
public E pop() {
return list.removeFirst();
}
@Override
public E peek() {
return list.getFirst();
}
@Override
public String toString() {
StringBuilder res=new StringBuilder();
res.append("Stack: top");
res.append(list);
return res.toString();
}
public static void main(String[] args) {
LinkedListStack<Integer> stack=new LinkedListStack<>();
for (int i = 0; i <5; i++) {
stack.push(i);
System.out.println(stack);
}
stack.pop();
System.out.println(stack);
}
}
运行结果:
Stack: top0->NULL
Stack: top1->0->NULL
Stack: top2->1->0->NULL
Stack: top3->2->1->0->NULL
Stack: top4->3->2->1->0->NULL
Stack: top3->2->1->0->NULL
思考一下 链表实现的栈和数组实现的栈哪个时间复杂度更低?
测试一下
import java.util.Random;
public class Main {
//测试Stack运行opCount个push和pop操作所需要的时间,单位:秒
private static double testStack(Stack<Integer> stack, int opCount){
long startTime=System.nanoTime();
Random random=new Random();
for (int i = 0; i < opCount; i++)
stack.push(random.nextInt(Integer.MAX_VALUE));
for (int i = 0; i < opCount; i++)
stack.pop();
long endTime=System.nanoTime();
return (endTime-startTime)/1000000000.0;
}
public static void main(String[] args) {
int opCount=100000;
ArrayStack<Integer> arrayStack=new ArrayStack<>();
double time1=testStack(arrayStack,opCount);
System.out.println("ArrayStack, time: " + time1 + " s");
LinkedListStack<Integer> linkedListStack=new LinkedListStack<>();
double time2=testStack(linkedListStack,opCount);
System.out.println("LinkedListStack, time: " + time2 + " s");
//实际中这个时间更加复杂,因为LinkedListStack中包含了更多的new操作
}
}
运行结果:
ArrayStack, time: 0.016972503 s
LinkedListStack, time: 0.020020522 s
根据不同操作系统不同内部因素影响,这两者的比较还是相对复杂的,但总体来说LinkedListStack耗时更长,因为它会不停地进行new操作,当然还有许多原因,不去细究。
将opCount换成10000000的结果
ArrayStack, time: 2.662845123 s
LinkedListStack, time: 4.303465165 s
总之,两者之间没有巨大的复杂度差异,后者略微逊色一点。
不用头结点实现,对首负责出队,对尾负责出队。
Queue接口
public interface Queue<E> {
int getSize();
boolean isEmpty();
void enqueue(E e );
E dequeue();
E getFront();
}
LinkedListQueue.java
public class LinkedListQueue<E> implements Queue<E> {
private class Node {
public E e;
public Node next;
public Node(E e, Node next) {
this.e = e;
this.next = next;
}
public Node(E e) {
this(e, null);
}
public Node() {
this(null, null);
}
@Override
public String toString() {
return e.toString();
}
}
private Node head, tail;
private int size;
public LinkedListQueue(){
head=null;
tail=null;
size=0;
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return size==0;
}
@Override
public void enqueue(E e) {
if(tail==null){
tail=new Node(e);
head=tail;
}else{
tail.next=new Node(e);
tail=tail.next;
}
size++;
}
@Override
public E dequeue() {
if(isEmpty())
throw new IllegalArgumentException("Connot dequeue from an empty enqueue");
Node retNode=head;
head=head.next;
retNode.next=null;
//如果队列中只会有一个元素,要维护tail,不然tail指向的是retNode,而不是null
if(head==null){
tail=null;
}
size--;
return retNode.e;
}
@Override
public E getFront() {
if(isEmpty())
throw new IllegalArgumentException("Connot dequeue from an empty enqueue");
return head.e;
}
@Override
public String toString() {
StringBuilder res=new StringBuilder();
res.append("Queue:front");
Node cur=head;
while(cur!=null){
res.append(cur.e+"->");
cur=cur.next;
}
res.append("NULL tail");
return res.toString();
}
public static void main(String[] args) {
LinkedListQueue<Integer> queue=new LinkedListQueue<>();
for (int i = 0; i < 10; i++) {
queue.enqueue(i);
System.out.println(queue);
if(i%3==2){
queue.dequeue();
System.out.println(queue);
}
}
}
}
运行结果: