目录
一.队列
1 概念
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(FirstIn First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头(Head/Front)
继承了两个接口
一个是Queue(队列)
另一个是Deque(双端队列)
实现的方法比较少 只是普通队列
就是双端队列
2.实现
1.底层原理
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。
面试问题:
ArrayList和LinkList的区别是什么
从1.增删查改出发
2.内存的逻辑来说.
3.队列是没有下标的
(10条消息) ArrayList和LinkList的区别_只想撸铁的博客-CSDN博客
arraylist和linkedlist
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。 这一点要看实际情况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。
这里我们用到单向链表举例,源码是用的双向链表
如果用单向链表实现先进先出
那么就是队尾进,对头出的形式
那么
尾插法入队,头插法出队.这样时间复杂度都是O(1).
2.方法的实现
class Node{
int val;
Node next;
public Node(int val){
this.val=val;
}
}
public class MyLinkedList {
public Node head;
public Node last;
/**入队
* w尾插法
* val
* val
*/
public void offer(int val){
Node node=new Node(val);
if(this.head==null){
this.head=node;
this.last=node;
}else{
this.last.next=node;
this.last=node;
}
}
/**出队
* 头节点
*
*/
public int poll(){
if(this.head==null){
throw new RuntimeException("队列为空");
}
int oldVal=this.head.val;
this.head=this.head.next;
return oldVal;
}
public boolean isEmpty(){
return this.head==null;
}
public int peek(){
if(isEmpty()){
throw new RuntimeException("队列为空");
}
return this.head.val;
}
}
二.循环队列
1.底层实现
数组
这里我们的实现就是用数组
用front定义对头
用real定义队尾
假设数组有十个元素
那么怎么构成循环呢
数组下标循环的小技巧
- 下标最后再往后(offset 小于 array.length): index = (index + offset) % array.length
- 下标最前再往前(offset 小于 array.length): index = (index + array.length - offset) % array.length
2.判断空还是满
但是我们有个疑问.
如果rear和front相遇了,到底是空的还是满的.
第一种情况
这两种情况都是相遇了
解决方法
1.用usedSize.用usedSize与数组长度比较.确定满或者空
2.用标志位.定义flag=false
入队列,没放一个元素,就置为true.
出队列.每出一个元素,就置为false.
第三种情况
每次存放元素之前,都先检查一下rear的下一个是不是front.如果是那就是满的
我们最后一个格子不放任何元素,用来作为判断是否满
3.循环队列的实现
数组下标循环的小技巧
- 下标最后再往后(offset 小于 array.length): index = (index + offset) % array.length
- 下标最前再往前(offset 小于 array.length): index = (index + array.length - offset) % array.length
class MyCircularQueue {
int[] elem;
int front;//对头下标
int real;//队尾下标
public MyCircularQueue(int k) {
this.elem=new int[k+1];//队列长度是k那么就可以放k+1
//因为我们这次设计的队列是会有一个空格子来判断是否满了
}
/**
* 入队
* @param value
* @return
*/
public boolean enQueue(int value) {
if(isFull()) return false;
elem[real]=value;
real= (real+1)%elem.length;
return true;
}
/**
* 出队
* @return
*/
public boolean deQueue() {
if(isEmpty()) return false;
// elem[front]=elem[front+1];
front=(front+1)% elem.length;
return true;
}
public int Front() {
if(isEmpty()) {
return -1;
}
return elem[front];
}
public int Rear() {
if(isEmpty()) {
return -1;
}
int index=0;
if(real==0){
index=elem.length-1;
}else{
index=real-1;
}
return elem[index];
}
public boolean isEmpty() {
return front==real;
}
public boolean isFull() {
if((this.real+1)%elem.length==front){
return true;//判断下一个是否是
}
return false;
}
}
三.Java基础数据类型的默认值
1、整数类型(byte、short、int、long)的基本类型变量的默认值为0。
2、单精度浮点型(float)的基本类型变量的默认值为0.0f。
3、双精度浮点型(double)的基本类型变量的默认值为0.0d。
4、字符型(char)的基本类型变量的默认为 “/u0000”。
5、布尔性的基本类型变量的默认值为 false。
6、引用类型的变量是默认值为 null。
7、数组引用类型的变量的默认值为 null。除关键数组变量的实例后,如果没有没有显示的为每个元素赋值,Java 就会把该数组的所有元素初始化为其相应类型的默认值。
四.队列和栈的转换
栈的特点是先进后出
而队列的特点是先进先出
如果我们用两个队列来实现的话
push可以正常用
但是在pop的时候要注意,因为对于队列来说pop的在栈里实现就是最后一个元素
所以我们需要把所有的都pop到另外一个空的队列,再pop出最后一个,也就是栈的第一个
这个题目的关键就是一定要保证至少有一个队列是空的
class MyStack {
LinkedList<Integer> lisk1;
LinkedList<Integer> lisk2;
public MyStack() {
lisk1=new LinkedList();
lisk2=new LinkedList();
}
public void push(int x) {
if(!lisk1.isEmpty()){
lisk1.offer(x);
}else if(!lisk2.isEmpty()){
lisk2.offer(x);
}else{
lisk1.offer(x);
}
}
public int pop() {
int val=0;
if(!lisk1.isEmpty()){
int size=lisk1.size();
for(int i = 0;i<size-1;i++){
val=lisk1.poll();
lisk2.offer(val);
}
return lisk1.poll();
}else{
int size=lisk2.size();
for(int i =0;i<size-1;i++){
val=lisk2.poll();
lisk1.offer(val);
}
return lisk2.poll();
}
}
public int top() {
int val=0;
if(!lisk1.isEmpty()){
int size=lisk1.size();
for(int i = 0;i<size;i++){
val=lisk1.poll();
lisk2.offer(val);
}
return val;
}else{
int size=lisk2.size();
for(int i =0;i<size;i++){
val=lisk2.poll();
lisk1.offer(val);
}
return val;
}
}
public boolean empty() {
return lisk1.isEmpty()&&lisk2.isEmpty();
}
}
五.二维网格迁移
此题我们仔细研究,如果把整个二维数组平铺成一个一维数组
那就是每个元素向右移动一次,进行一个循环,这里就可以跟今天学的循环队列联系上了
我们可以把二维数组的每一个移动号的元素放在一维数组里,
从0开始模拟放元素,对于一维数组就是移动后的k位置,要对k进行取模,再k++
因为k位置超过长度后就又回到0.所以每次
都要取模,不能只取模一次'
这是这道题的精华
class Solution {
public List<List<Integer>> shiftGrid(int[][] grid, int k) {
//第一步,先平铺成一维数组,因为这个题的意思,就是向右移动多少位
int len=grid.length * grid[0].length;//总长度
int[] nums = new int[len];//行和列相乘
int iMax = grid.length;//行的长度
int jMax = grid[0].length;//列的长度
for(int i = 0;i < iMax;i++) {//iMax总长度
for(int j = 0;j < jMax;j++) {//第六行k %= nums.length,如果k等于nums.length就
k%=len;//就是0,k大于nums.length就是余数
nums[k++] = grid[i][j];//相当于一个圆.要去掉圆的圈数就是移动的次数
//然后开始从0模拟
}//如果超过一维数组本身的长度,等于就是从0,大于就是取余.
//太巧妙了.这个做法!
}
//因为题目要的是顺序表,就放到顺序表里.
k=0;
List<List<Integer>> lists=new ArrayList<>(grid.length);
for(int i = 0;i < iMax;i++) {//iMax总长度
List<Integer> tempList = new ArrayList<>(grid[0].length);//每次循环都建立一个新的表
for(int j = 0;j < jMax;j++) {
tempList.add(nums[k++]);
}//二维网格就放好了
lists.add(tempList);//把表放入表中
}
return lists;
}
六.将数组分成相等的三部分
这道题一定要注意是跟剧索引来的.也就是连续的
我们的思路.就是先算出总大小,然后看能不能整除3,不能纠错了
然后遍历数组,看能不能分成三等分,其实遇到两组的时候就可以返回TRUE了
因为进入循环一定整除3/既然找到前两组,最后一组一定也是
如果还跳出了循环就说明是FALSE了
我今天还有另外一种情况
就是循环出来,如果是三组或者大于3但是整个等于0代表就是全是0那就是true
但是总有一个不对
class Solution {
public boolean canThreePartsEqualSum(int[] arr) {
int sum=0;
for(int i:arr){
sum+=i;
}//这里算出了数组的总长度
//这个题目要注意是连续的索引,不可以是跳过的,这样就很简单了
if(sum%3!=0){
return false;//说明不能整除3
}
int goal=sum/3;
int i=0;
sum=0;
for(int j=0;j<arr.length;j++){
sum+=arr[j];
if(i==2){
return true;
}
if(sum==goal){
i++;
sum=0;
}
}
return false;
}
}