顺序表
顺序表:顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可以分为:
- 静态顺序表:使用定长数组存储。
- 动态顺序表:使用动态开辟的数组存储。
静态顺序表适用于确定知道需要存多少数据的场景,静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。
相比之下动态顺序表更灵活, 根据需要动态的分配空间大小。
动态顺序表实现:
/**
* @author 枳洛淮南
* @version 1.0
* @Description 顺序表
* @Date 2021/8/15 17:43
*/
public class SequenceTable
{
private int[] elem;
private int usedSize;
public SequenceTable()
{
this.elem = new int[10];
}
public SequenceTable(int[] elem)
{
this.elem = elem;
}
/**
* 打印顺序表
**/
public void display()
{
for (int i = 0; i < this.usedSize; i++)
{
System.out.print(this.elem[i]);
if (i != this.usedSize - 1)
{
System.out.print(" --> ");
}
}
System.out.println();
}
/**
* 在 pos 位置新增元素
**/
public void add(int pos, int data)
{
if (this.usedSize == this.elem.length)
{
//扩容:位运算符优先级太低需要加括号
int[] ret = new int[this.usedSize + (this.usedSize << 1)];
System.arraycopy(this.elem, 0, ret, 0, this.usedSize);
this.elem = ret;
}
if (pos < 0 || pos > this.usedSize)
{
throw new ArrayIndexOutOfBoundsException("下标不合法:" + pos);
}
//下标合法且容量足够
int i = this.usedSize;
while (i > pos)
{
this.elem[i] = this.elem[--i];
}
this.elem[pos] = data;
this.usedSize++;
}
/**
* 判定是否包含某个元素
**/
public boolean contains(int toFind)
{
for (int i : this.elem)
{
if (i == toFind)
{
return true;
}
}
return false;
}
/**
* 查找某个元素对应的位置
**/
public int search(int toFind)
{
for (int i = 0; i < this.usedSize; i++)
{
if (toFind == this.elem[i])
{
return i;
}
}
return -1;
}
/**
* 获取 pos 位置的元素
**/
public int getPos(int pos)
{
if (pos < 0 || pos >= this.usedSize)
{
throw new ArrayIndexOutOfBoundsException("下标不合法:" + pos);
}
return this.elem[pos];
}
/**
* 给 pos 位置元素设为 value
**/
public void setPos(int pos, int value)
{
if (pos < 0 || pos >= this.usedSize)
{
throw new ArrayIndexOutOfBoundsException("下标不合法:" + pos);
}
this.elem[pos] = value;
}
/**
* 获取顺序表长度
**/
public int size()
{
return this.usedSize;
}
/**
* 删除第一次出现的 元素
**/
public void remove(int toRemove)
{
//存在则删除
int index = this.search(toRemove);
if (index != -1)
{
// for (int i = index; i < this.usedSize - 1; i++)
// {
// this.elem[i] = this.elem[i + 1];
// }
while (index < this.usedSize - 1)
{
this.elem[index] = this.elem[index + 1];
index++;
}
this.usedSize--;
} else
{
throw new RuntimeException("该元素不存在:" + toRemove);
}
}
/**
* 清空顺序表
**/
public void clear()
{
this.usedSize = 0;
}
}
单链表
链表:链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。
链表实现
import java.util.Stack;
class Node
{
public int data;
public Node next;
public Node(int data, Node next)
{
this.data = data;
this.next = next;
}
public Node(int data)
{
this.data = data;
}
public Node()
{
}
}
/**
* @author 枳洛淮南
* @version 1.0
* @Description // 1、无头单向非循环链表实现
* @Date 2021/8/15 21:30
*/
public class SingleLinkedList
{
Node head;
/**
* 头插法
**/
public void addFirst(int data)
{
Node node = new Node(data);
node.next = this.head;
this.head = node;
}
/**
* 尾插法
**/
public void addLast(int data)
{
Node node = new Node(data);
//空链表
if (this.head == null)
{
this.head = node;
} else
{
Node cur = this.head;
while (cur.next != null)
{
cur = cur.next;
}
cur.next = node;
}
}
/**
* 任意位置插入,第一个数据节点为0号下标
**/
public void addIndex(int index, int data)
{
if (index < 0 || index > this.size())
{
throw new RuntimeException("输入不合法");
}
if (this.head == null || 0 == index)
{
this.addFirst(data);
return;
}
Node node = new Node(data);
Node cur = this.head;
while (index-- > 1)
{
cur = cur.next;
}
node.next = cur.next;
cur.next = node;
}
/**
* 查找是否包含关键字key是否在单链表当中
**/
public boolean contains(int key)
{
Node cur = this.head;
while (cur != null)
{
if (cur.data == key)
{
//有这个数字
return true;
}
cur = cur.next;
}
//没有
return false;
}
/**
* 删除第一次出现关键字为key的节点
**/
public void remove(int key)
{
if (this.contains(key))
{
if (this.head.data == key)
{
this.head = this.head.next;
return;
}
Node cur = head;
boolean flag = false;
while (cur.next != null)
{
if (cur.next.data == key)
{
flag = true;
break;
}
cur = cur.next;
}
if (flag)
{
cur.next = cur.next.next;
}
} else
{
throw new NullPointerException("单链表中为该元素" + key);
}
}
/**
* 删除所有值为key的节点
**/
public void removeAllKey(int key)
{
if (this.head != null)
{
while (this.head != null && this.head.data == key)
{
this.head = this.head.next;
}
//头结点必然不为要删除的节点
if (this.head != null)
{
Node prev = this.head;
Node cur = this.head.next;
while (cur != null)
{
if (cur.data != key)
{
prev.next = cur;
prev = prev.next;
}
cur = cur.next;
}
prev.next = null;
}
} else
{
throw new NullPointerException("单链表为空");
}
}
/**
* 反转单链表
**/
public void reverseList()
{
Node cur = this.head;
Node prev = null;
while (cur != null)
{
Node node = cur.next;
cur.next = prev;
prev = cur;
cur = node;
}
this.head = prev;
}
/**
* 链表的中间结点
**/
public Node middleNode()
{
Node p = this.head;
Node q = this.head;
while (p != null && p.next != null)
{
p = p.next.next;
q = q.next;
}
return q;
}
/**
* 删除链表的倒数第 k 个节点
**/
public void removeNthFromEnd(int n)
{
if (n < 0 || this.head == null)
{
return;
}
Node p = this.head;
Node q = this.head;
for (int i = 0; i < n; i++)
{
if (p != null)
{
p = p.next;
} else
{
return;
}
}
Node prev = new Node(-1);
while (p != null)
{
prev.next = q;
prev = prev.next;
q = q.next;
p = p.next;
}
prev.next = q.next;
}
/**
* 倒数第 k 个结点
**/
public Node getKthFromEnd(int k)
{
if (k < 0 || this.head == null)
{
return null;
}
Node p = this.head;
Node q = this.head;
for (int i = 0; i < k; i++)
{
if (p != null)
{
p = p.next;
} else
{
return null;
}
}
while (p != null)
{
q = q.next;
p = p.next;
}
return q;
}
/**
* 得到单链表的长度
**/
public int size()
{
int length = 0;
Node cur = this.head;
while (cur != null)
{
cur = cur.next;
length++;
}
return length;
}
/**
* 打印单链表
**/
public void display()
{
Node cur = this.head;
while (cur != null)
{
System.out.print(cur.data);
cur = cur.next;
if (cur != null)
{
System.out.print(" --> ");
}
}
System.out.println();
}
/**
* 分隔链表
**/
public Node partition(Node head, int x)
{
//头结点为空或只有一个结点
if (head == null || head.next == null)
{
return head;
}
Node cur = head;
Node smallCurrent = new Node(-1); //傀儡小结点
Node smallHead = smallCurrent;
Node largeCurrent = new Node(-1); //傀儡大结点
Node largeHead = largeCurrent;
while (cur != null)
{
if (cur.data < x)
{
smallCurrent.next = cur;
smallCurrent = smallCurrent.next;
} else
{
largeCurrent.next = cur;
largeCurrent = largeCurrent.next;
}
cur = cur.next;
}
smallCurrent.next = largeHead.next;
largeCurrent.next = null;
return smallHead.next;
}
/**
* 合并两个有序链表
**/
public Node mergeTwoLists2(Node l1, Node l2)
{
if (l1 == null)
{
return l2;
}
if (l2 == null)
{
return l1;
}
Node head = new Node();
//确定头结点
if (l1.data < l2.data)
{
head = new Node(l1.data);
l1 = l1.next;
} else
{
head = new Node(l2.data);
l2 = l2.next;
}
Node cur = head;
while (l1 != null && l2 != null)
{
Node temp = null;
if (l1.data < l2.data)
{
temp = new Node(l1.data);
l1 = l1.next;
} else
{
temp = new Node(l2.data);
l2 = l2.next;
}
cur.next = temp;
cur = cur.next;
}
if (l1 == null)
{
cur.next = l2;
} else
{
cur.next = l1;
}
return head;
}
public Node mergeTwoLists1(Node l1, Node l2)
{
if (l1 == null)
{
return l2;
}
if (l2 == null)
{
return l1;
}
Node ret = l1.data < l2.data ? l1 : l2;
ret.next = mergeTwoLists1(ret.next, l1.data >= l2.data ? l1 : l2);
return ret;
}
/**
* 删除重复元素:重复结点保留
**/
public void deleteDuplicates()
{
if (this.head == null || this.head.next == null)
{
throw new RuntimeException("头结点为空或单链表只有一个结点");
}
Node cur = this.head;
Node prev = this.head;
while (cur != null)
{
if (prev.data != cur.data)
{
prev.next = cur;
prev = prev.next;
}
cur = cur.next;
}
prev.next = null;
}
/**
* 两个链表的第一个公共结点
*/
public Node getIntersectionNode(Node headA, Node headB)
{
if (headA == null || headB == null)
{
return null;
}
Node pa = headA, pb = headB;
while (pa != pb)
{
pa = pa == null ? headB : pa.next;
pb = pb == null ? headA : pb.next;
}
return pa;
}
/**
* 判断链表是否有环
**/
public boolean hasCycle()
{
Node fast = this.head, slow = this.head;
while (fast != null && fast.next != null)
{
fast = fast.next.next;
slow = slow.next;
if (fast == slow)
{
return true;
}
}
return false;
}
/**
* 链表入环后的第一个结点
*/
public Node detectCycle()
{
Node fast = this.head, slow = this.head;
while (slow != null)
{
if (fast == null || fast.next == null)
{
return null;
}
fast = fast.next.next;
slow = slow.next;
if (fast == slow)
{
break;
}
}
if (fast == null)
{
//没有环
return null;
}
fast = this.head;
while (fast != slow)
{
fast = fast.next;
slow = slow.next;
}
return fast;
}
/**
* 判断链表是否为回文链表
**/
public boolean isPalindrome()
{
if (this.head == null || this.head.next == null)
{
return true;
}
Stack<Integer> stack = new Stack<>();
Node cur = this.head;
while (cur != null)
{
stack.push(cur.data);
cur = cur.next;
}
cur = this.head;
while (!stack.isEmpty())
{
if (cur.data != stack.pop())
{
return false;
}
cur = cur.next;
}
return true;
}
/**
* 删除重复结点:重复结点不保存
**/
public void deleteDuplicatesNone()
{
if (this.head == null)
{
throw new RuntimeException("头结点为空");
}
Node newHead = new Node(-1);
newHead.next = head;
Node cur = newHead;
while (cur.next != null && cur.next.next != null)
{
if (cur.next.data == cur.next.next.data)
{
int num = cur.next.data;
while (cur.next != null && cur.next.data == num)
{
cur.next = cur.next.next;
}
} else
{
cur = cur.next;
}
}
this.head = newHead.next;
}
/**
* 清空单链表
**/
public void clear()
{
this.head = null;
}
}
区别
顺序表:
优点:空间连续、支持随机访问
缺点:
-
中间或前面部分的插入删除时间复杂度O(N)
-
增容的代价比较大。
链表:
缺点:以节点为单位存储,不支持随机访问
优点:
1.任意位置插入删除时间复杂度为O(1)
2.没有增容问题,插入一个开辟一个空间。