练习
用队列实现栈
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:
void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
注意:
你只能使用队列的基本操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。
你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
示例:
输入:
["MyStack", "push", "push", "top", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]
解释:
MyStack myStack = new MyStack();
myStack.push(1); 栈里 1
myStack.push(2); 栈里 1 2
myStack.top(); // 返回 2 返回最后的栈顶 不删除
myStack.pop(); // 返回 2返回最后的栈顶 删除
myStack.empty(); // 返回 False 栈里剩下1
提示:
1 <= x <= 9
最多调用100 次 push、pop、top 和 empty
每次调用 pop 和 top 都保证栈不为空
package easy.a用队列实现栈2;
import java.util.LinkedList;
import java.util.Queue;
/**
*
*/
class MyStack {
private Queue<Integer> queue;
/** Initialize your data structure here. */
public MyStack() {
queue = new LinkedList<>();
}
public static void main(String[] args) {
MyStack myStack = new MyStack();
myStack.push(1);
System.out.println(" ");
System.out.println("下一行");
myStack.push(2);
System.out.println(" ");
System.out.println("下一行");
myStack.push(3);
System.out.println(" ");
System.out.println("下一行");
myStack.push(4);
// myStack.top();
System.out.println(myStack.top());
// myStack.pop();
System.out.println(myStack.pop());
myStack.empty();
System.out.println(myStack.queue);
}
/**
* 在添加数据的时候进行翻转
*/
/** Push element x onto stack. */
public void push(int x) {
Queue<Integer> temp = new LinkedList<>();
while (!queue.isEmpty()) {
System.out.println("queue:"+queue);
temp.add(queue.poll());
System.out.println("TEMP:"+temp);
}
System.out.println("------------");
queue.add(x);
System.out.println(queue);
System.out.println("------------");
while (!temp.isEmpty()) {
System.out.println("TEMP:"+temp);
queue.add(temp.poll());
System.out.println("queue:"+queue);
}
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
return queue.poll();
}
/** Get the top element. */
public int top() {
return queue.peek();
}
/** Returns whether the stack is empty. */
public boolean empty() {
return queue.isEmpty();
}
}
上述演示最后一次push
介绍
栈底是第一个进栈的数据,栈顶就是最后一个进栈的数据
poll是队列数据结构实现类的方法,从队首获取元素,同时获取的这个元素将从原队列删除
peek()方法用于从此Stack中返回顶部元素,并且它不删除就检索元素
栈:是一种特殊的线性表,其只允许在一端出数据和入数据,插入数据和删除数据的一端加栈顶,另一端叫栈底。其数据遵守先进后出的原则(Last in First out)
总结
从结果来看 好像是倒叙的
但是栈不能遍历,要访问下一个数据,必须把现在的数据弹出栈顶。
如果是一边访问数据一边输出,还是以1 2 3 4 出现
扩展
队列:队列具有先进先出,进行插入操作的一端称为队尾。进行删除操作的一端称为队首。
因为其只能在队头出数据,所以进入一段数据 1 2 3 4 ,那么出数据的顺序也该是 1 2 3 4
如果加入5 则在4后面加入
小玉家的电费
夏天到了,各家各户的用电量都增加了许多,相应的电费也交的更多了。小玉家今天收到了一份电费通知单。小玉看到上面写:据闽价电[2006]27号规定,月用电量在150千瓦时及以下部分按每千瓦时0.4463元执行,月用电量在151~400千瓦时的部分按每千瓦时0.4663元执行,月用电量在401千瓦时及以上部分按每千瓦时0.5663元执行;小玉想自己验证一下,电费通知单上应交电费的数目到底是否正确呢。请编写一个程序,已知用电总计,根据电价规定,计算出应交的电费应该是多少。
输入描述:
输入一个整数,表示用电总计(单位以千瓦时计),不超过10000。
输出描述:
输出一个数,保留到小数点后1位(单位以元计,保留到小数点后1位
import java.util.ArrayList;
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String str_0 = scan.nextLine().trim();
int amount = Integer.parseInt(str_0);
scan.close();
float result = solution(amount);
System.out.println(String.format("%.1f", result));
}
public static float solution(int amount){
float result = (float)0.0;
if(0<=amount&&amount<=150){
result=(float)(amount*0.4463);
}else if(150<amount&&amount<=400){
result=(float)(150*0.4463+(amount-150)*0.4663);
}else if(400<amount&&amount<=10000){
result=(float)(150*0.4463+250*0.4663+(amount-400)*0.5663);
}
return result;
}
}
结论: 1.&& 2.输出时使用 String.format("%.1f", result) 3.强制类型转换
查找整数
题目描述
给定一个非降序的整数数组,数组中包含重复数字(重复数字很多) ,给定任意整数,对数组进行二分查找,返回数组正确的位置,给出函数实现。 a. 连续相同的数字,返回最后一个匹配的位置 b. 如果数字不存在返回 -1。(测试用例仅做参考,我们会根据代码质量进行评分)
输入描述:
第一行给定数组长度n,目标值tar。(1<=n,tar<=10000) 第二行给出n个整数a.(1<=a<=10000)
输出描述:
按题目描述输出。
示例
示例1
输入
7 4
1 2 2 3 4 4 10
输出
5
import java.util.ArrayList;
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String str_0 = scan.nextLine();
String[] line_list_0 = str_0.trim().split(" ");
ArrayList<Integer> arr_temp = new ArrayList<>();
for(int i = 0; i < line_list_0.length; i++){
arr_temp.add(Integer.parseInt(line_list_0[i]));
}
int n = arr_temp.get(0);//数组长度
int tar = arr_temp.get(1);//目标值
String str_2 = scan.nextLine();
String[] line_list_2 = str_2.trim().split(" ");
ArrayList<Integer> arr = new ArrayList<>();
for(int i = 0; i < line_list_2.length; i++){
arr.add(Integer.parseInt(line_list_2[i]));
}//一堆数字
scan.close();
int result = solution(n, tar, arr);
System.out.println(result);
}
public static int solution(int n, int tar, ArrayList<Integer> arr){
int result = 0;
int left=0;int right=n-1;int mid=0;
while(left<=right){//如果最后的下标大于等于左边的下标
mid=(left+right)/2;//二分查找精华 取中值下标
if(tar==arr.get(mid)){//如果值相等
if(mid==0){//判断下标为0的情况
return right;
}
if(mid!=right) {//如果中值下标与边界不相等
while (arr.get(mid).equals(arr.get(mid + 1))) {//判断中值和后一个下标值是否一致
mid++;//一致就自增
}
if (!arr.get(mid).equals(arr.get(mid + 1))) {//是否不一致
result = mid;//可以把result去了 好像是刷题自带的 没用上
return result;
}
}else {
return right;//如果相等了 就直接返回下标 因为已经边界了
}
}else if (tar<arr.get(mid)) {//二分查找精华 目标值小于中间值 就right变化
right=mid-1;
}else {//二分查找精华 目标值大于中间值 就left变化
left=mid+1;
}
}
return -1;//没查到
}
}
总结:
二分查找,查找的是顺序队列的,无序查不出来
数组有n个 下标是n-1个 因为是从0开始
考虑有无输入的数字 看看后面的数字是否与前面相同
要考虑下标为0和1的情况
可能还可以简化,新手学习,请多指点。
八股
hashMap 链表长度大于8后一定会转为红黑树吗?
不一定,如果链表长度大于8同时数组长度大于64才会变成红黑树,数组小于64会进行扩容,这种情况下数组加链表比红黑树更有效率,查询速度更快。
红黑树的元素小于6一定会变成链表吗?
不一定,如果出现resize(改变尺寸大小)的时候才会根据untreeify_threshold(取消验证_阈值)进行转换
concurrentMashMap(线程安全)分段锁之间加了什么锁,读写之间是互斥的吗?
1.7
使用分段锁(Segment)来控制并发访问。每个段都是一个独立的哈希表,只包含总容量的一部分,每个段都有自己的锁,因此不同的线程可以同时访问不同的段
1.8
它使用CAS(Compare and Swap)和synchronized来取代分段锁。这种实现方式被称为"数组+链表+红黑树",即每个桶内部采用链表或红黑树来存储键值对,当桶内键值对数量达到一定阈值时,链表会自动转化为红黑树,以提高查找效率。
整个哈希表被分成若干个段,每个段都被实现为一个数组,每个数组元素都是一个链表或红黑树。每个元素都是一个桶,通过哈希函数将键值对映射到桶中。每个桶内部都通过synchronized来实现同步,以保证线程安全性。而对于读操作,使用了无锁的CAS操作。
类型 | JDK1.7 | JDK1.8 |
数据结构 | Segment分段锁 | 数组+链表+红黑树 |
线程安全机制 | segment的分段锁机制 | CAS+Synchorized机制 |
锁的粒度 | 每段Segment加锁,粒度大 | 每个Node元素加锁,粒度更细 |
减少锁竞争:由于每个桶内部采用了synchronized来实现同步,不同的线程可以同时访问不同的桶,从而减少了锁竞争。
更高的并发度:由于不再受限于固定数量的段,ConcurrentHashMap 可以根据需要动态调整大小,并支持更高的并发度。
更好的扩展性:由于不再需要维护多个段的锁,因此在扩展时可以更容易地添加或删除桶,而不需要重构整个数据结构。
更好的性能:使用CAS操作替代了分段锁,避免了分段锁中的自旋等待开销,提高了并发性能。
三次握手后一直不连接会怎么样?
服务器会认为丢包,然后向客户端发送大量syn包重传,产生半连接,如果客户端一直不发送数据,也就是不回送ack包, 就会让服务器产生大量的半连接,从而占用服务器的大量资源
TCP/IP协议四层是什么?
数据链路层、网络层、传输层、应用层。
以太网的数据帧在链路层; IP包在网络层; TCP和UDP包在传输层; TCP和UDP中的数据在应用层
三次握手四次挥手介绍
三次握手
第一次客户端向服务端发送syn=1 seq=x 数据包 代表请求连接
第二次服务器收到数据包后回送 syn=1 seq=y ack=x+1的数据包 代表可以,允许连接
第三次客户端向服务器发送syn=1 seq=x+1 ack=y+1的数据包 代表收到,开始连接
三次握手连接成功 其中syn标志位数置1,表示建立TCP连接;ack标志表示验证字段。
四次挥手
第一次客户端向服务端发送fin=1 seq=x数据包 代表请求断开连接 然后变成fin_wait(等待之前发送的连接终止请求的确认)
第二次服务器向客户端发送fin=1 ack=x+1 seq=y数据包 代表可以断开,请等待 然后变成close_wait(等待本地用户的连接终止请求)
第三次服务器确保数据传输完成,再向客户端发送fin=1 ack=x+1 seq=z数据包 代表结束等待 变成lask_act并等待(lask_act:等待先前发送给远端TCP 的连接终止请求的确认(包括它字节的连接终止请求的确认))
第四次客户端确认后回送fin=1 ack=z+1 seq=h数据包 代表确认终止 并变成time_wait(等待足够的时间过去以确保远端TCP 接收到它的连接终止请求的确认)
四次挥手断开连接完成
HashMap的底层是数组+链表
jdk1.8底层是数组+链表或者数组+红黑树
HaspMap的数组默认大小为16
HashMap线程不安全