1.队列问题
- 队列是一个有序列表,可以用数组或是链表来实现。
- 遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出
1.1 数组模拟队列
1.1.1 单列队列
- MaxSize表示队列的容量
- 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 font 及 rear 分别记录队列前后端的下标,front 会随着数据输出而改变,而 rear 则是随着数据输入而改变。
- 当我们将数据存入队列时称为”addQueue”,addQueue 的处理需要有两个步骤: 思路分析
- 将尾指针往后移: rear+1,当 front == rear ->[空]
- 若尾指针 rear 小于队列的最大下标 maxSize-1,则将数据存入 rear 所指的数组元素中,否则无法存入数据。== maxSize - 1[队列满1
代码:
@Data
class ArrQuery {
private Integer masSize;
private Integer rear;
private Integer front;
private Integer[] arrData;
/**
* 初始化队列
*
* @param masSize
*/
ArrQuery(Integer masSize) {
this.masSize = masSize;
arrData = new Integer[masSize];
front = -1;
rear = -1;
}
/**
* 判断队列是否满
*/
private Boolean isFull() {
return masSize.equals(rear + 1);
}
/**
* 判断队列是否为空
*/
private Boolean isNull() {
return Objects.equals(front, rear);
}
/**
* 添加数据
*/
private Integer add(Integer data) {
if (this.isFull()) {
return -1;
}
//队尾后移
rear++;
//给当前队尾添加数据
arrData[rear] = data;
return rear;
}
/**
* 取出队列数据(先进先出 ,队头出队)
*/
private Integer getQuery() {
if (isNull()) {
return null;
}
//取出队头后移
front++;
return arrData[front];
}
}
但是这种单列队列有以下局限性:
- 一次性使用当队列满了再取出 front的下标索引后移无法还原->无法服用
1.2 循环数组模拟队列
- front不再指向-1,直接队列指向第一个数据。初始值=0
- rear指向队尾的后一个位置,初始值=0。
- 队列满的条件事:(rear+1)%maxSize==front。
- 队列为空的条件是 :rear==front。
- 队列有效数据数量:(rear+maxSize-front)%maxSize。
代码:
@Data
class AnnularArrQuery {
private Integer masSize;
private Integer rear;
private Integer front;
private Integer[] arrData;
/**
* 初始化队列
*
* @param masSize
*/
AnnularArrQuery(Integer masSize) {
this.masSize = masSize;
arrData = new Integer[masSize];
front = 0;
rear = 0;
}
/**
* 判断是否为空
*/
public Boolean isNull() {
return front == rear;
}
/**
* 是否满
*/
public Boolean isFull() {
return (rear + 1) % masSize == front;
}
/**
* 统计有效数据数量
*/
public Integer countData() {
return (rear + masSize - front) % masSize;
}
/**
* 添加
*/
public Integer add(Integer data) {
if (isFull()) {
return -1;
}
arrData[rear] = data;
//考虑取模
rear = (rear + 1) % masSize;
return rear - 1;
}
/**
* 取出队列 先入先出 队头先出
*/
public Integer get() {
if (isNull()) {
return null;
}
Integer data = arrData[front];
front = (front + 1) % masSize;
return data;
}
public void showQuery(){
if (isNull()){
System.out.println("null");
return;
}
//从当前头遍历 剩余数量的数据
for (int i=front;i<front+this.countData();i++){
System.out.printf("arr[%d]=%d\n",i%masSize,arrData[i%masSize]);
}
}
public Integer headQueue() {
if (isNull()){
return null;
}
return arrData[front];
}
}
这样就优化了上述的无法服用问题。
但是使用数组队列还有有一定的局限性:
- 空间有限。
- 空间过大早期会浪费空间。
- 如果做数组中的(删除/插入)操作需要迁移下标。
2.链表的使用
2.1 单链表
上面介绍的数组队列是地址空间连续的链式存储结构。
那么链表就是空间地址不联系的存储结构如图:
- 链表是以节点的方式来存储,是链式存储
- 每个节点包含 data 域, next 域: 指向下一个节点
- 发现链表的各个节点不一定是连续存储
- 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定
代码:
public class SingleLinkListDemo {
public static void main(String[] args) {
SingleLinkList linkList = new SingleLinkList();
linkList.addLinkNode(new LinkNode(new Person(1,"DFP",18)));
linkList.addLinkNode(new LinkNode(new Person(3,"GG",13)));
linkList.addLinkNode(new LinkNode(new Person(2,"CC",12)));
linkList.addLinkNode(new LinkNode(new Person(7,"qq",17)));
linkList.addLinkNodeById(new LinkNode(new Person(6,"rq",16)));
linkList.showLink();
linkList.deleteNode(3);
linkList.updateNodeById(new LinkNode(new Person(6,"up6",26)));
System.out.println("删除过后---------------------------");
linkList.showLink();
}
}
class SingleLinkList {
private LinkNode head = new LinkNode(new Person(-1, "", -1));
public Boolean addLinkNode(LinkNode data) {
//操作变量
LinkNode temp = head;
while (true) {
//找到队尾添加
if (temp.nextData == null) {
break;
}
//向后移
temp = temp.nextData;
}
temp.nextData = data;
return true;
}
public Boolean addLinkNodeById(LinkNode data) {
//操作变量
LinkNode temp = head;
while (true) {
if (temp.nextData.value.getId() == data.value.getId()) {
System.out.printf("Person id is %d have exit ", data.value.getId());
return false;
}
//从小到大排序插入 找到要插入的位置
if (temp.nextData.value.getId() > data.value.getId()) {
break;
}
//找到队尾添加
if (temp.nextData == null) {
break;
}
//向后移
temp = temp.nextData;
}
//插入操作
//1.将插入点的数据 接上当前节点后的数据
data.nextData = temp.nextData;
//2.将插入数据 接上当前节点
temp.nextData = data;
return true;
}
public void updateNodeById(LinkNode data) {
if (head.nextData == null) {
System.out.println("link is null");
return;
}
LinkNode temp = head.nextData;
while (true) {
if (Objects.equals(temp.value.getId(), data.value.getId())) {
temp.value.setAge(data.value.getAge());
temp.value.setName(data.value.getName());
break;
}
if (temp.nextData == null) {
System.out.println("dont have node id is " + data.value.getId());
break;
}
temp = temp.nextData;
}
}
public void deleteNode(Integer id) {
if (head.nextData == null) {
System.out.println("link is null");
return;
}
LinkNode curr = head.nextData;
while (true) {
//找到当前节点的下一个值
if (Objects.equals(curr.nextData.value.getId(), id)) {
//跳过目标节点,让当前节点的next指向下一个节点的next
curr.nextData = curr.nextData.nextData;
break;
}
if (curr.nextData == null) {
System.out.println("dont have node id is " + id);
break;
}
curr = curr.nextData;
}
}
public void showLink() {
if (head == null) {
System.out.println("Link:[]");
return;
}
LinkNode curr = head.nextData;
while (true) {
if (curr == null) {
break;
}
System.out.println(curr);
curr = curr.nextData;
}
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class LinkNode {
public Person value;
public LinkNode nextData;
LinkNode(Person person) {
this.value = person;
}
@Override
public String toString() {
return "LinkNode{" + value + '}';
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person {
private Integer id;
private String name;
private Integer age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", id=" + id +
'}';
}
}
计算链表有效数据:
/**
* 统计链表数据
*/
public Integer countSize() {
int size = 0;
LinkNode node = head.nextData;
if (node == null) {
return size;
}
while (node != null) {
size++;
node = node.nextData;
}
return size;
}
查找倒数第n个节点数据:
public LinkNode getLastNode(Integer n) {
LinkNode temp = head.nextData;
if (temp == null) {
System.out.println("dont have data");
return null;
}
//倒数第1 就是正数size-n
Integer index = this.countSize() - n;
if (n < 0 || index < 0) {
System.out.println("index is illegal");
return null;
}
for (int i = 0; i < index; i++) {
temp = temp.nextData;
}
return temp;
}
链表反转;
public void rollbackLink() {
LinkNode curr = this.head.nextData;
//空链表或者只有一个节点不需要反转
if (curr.nextData == null || curr.nextData.nextData == null) {
return;
}
LinkNode next = null;
LinkNode rollbackLink = new LinkNode(new Person(-1, "", -1), null);
while (curr!=null) {
//next 存放当前节点下一位置
next = curr.nextData;
//将当前节点尾部链接到 反转链表头节点的下一节点
curr.nextData = rollbackLink.nextData;
//将当前节点追加到 反转链表头部
rollbackLink.nextData = curr;
//当前链表后移
curr=next;
}
//链表互换
head.nextData=rollbackLink.nextData;
}