约瑟夫问题
单项环形链表:
尾部添加,添加进来的结点记得指向头结点实现环形
删除结点还是常规,始终找到删除节点的前一个结点
上代码:
package com.DataStructure._04LinkedList;
/**
* 单向环形链表
* 约瑟夫问题:
* Josephu 问题为:设编号为1,2,...n的n个人围坐一圈,约定编号为k (1<=k<=n)的人从1开始报数,
* 数到m的那个人出列,它的下一位接着数到m 的那个人又出列,
* 依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
*/
public class JosepfuCircleSingleListDemo {
public static void main(String[] args) {
JosepfuCircleSingleList josepfuCircleSingleList = new JosepfuCircleSingleList();
josepfuCircleSingleList.addBoy(5);
josepfuCircleSingleList.showBoys();
System.out.println("----出圈");
josepfuCircleSingleList.countBoy(1,2,5);//2-4-1-5-3
}
}
//创建一个单项环形链表
class JosepfuCircleSingleList {
/**
* 几个重要的点
* first结点---第一个结点,固定不变的
* curBoy结点---尾结点,指向新节点后定时挪动到尾部
*/
private Boy first = null;
public void addBoy(int num) {
if (num < 1) {
System.out.println("num数据不正确");
return;
}
Boy curBoy = null;//临时指针
for (int i = 1; i <= num; i++) {
Boy boy = new Boy(i);
if (i == 1) {
first = boy;
first.next = first;//闭环
curBoy = first;//
} else {
curBoy.next = boy;
boy.next = first;
curBoy = boy;
}
}
}
//遍历
void showBoys() {
Boy tempBoy = first;
while (true) {
System.out.println(tempBoy);
if (tempBoy.next == first) {
break;
}
tempBoy = tempBoy.next;
}
}
//报数和出列
/**
* @param startNo 开始小孩是几?
* @param countNum 规定报数是?
* @param nums 全部小孩的总数
* first -- 出列
* helper -- first前面一个(要删除结点的前一个位置)
*/
public void countBoy(int startNo, int countNum, int nums) {
//数据校验
if (startNo < 1 || first == null || startNo > nums) {
System.out.println("参数错误!");
return;
}
//first定位到startNo
for (int j = 0; j < startNo - 1; j++) {
//first默认是第一个小孩,移动startNo-1次 到达要求的starNo的位置
first = first.next;
}
//创建辅助结点helper,起始位置在first前一个
// (first就是要删除的结点,本质就是删除结点的前一个结点),后面一个一直保证helper在first前一个位置
Boy helper = first;
while (true) {
if (helper.next == first) {
break;
}
helper = helper.next;
}
//按照规则出列,直到只有一个结点
while (helper != first) {
//根据报数,移动countNum-1
for (int j = 0; j < countNum - 1; j++) {
first = first.next;
helper = helper.next;
}
//出列(删除结点)
System.out.println(first);
first = first.next;
helper.next = first;
}
System.out.println(helper);//最后一个小孩节点
}
}
//小孩的结点
class Boy {
public int no;
public Boy next;//指向下一个结点
public Boy(int no) {
this.no = no;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public Boy getNext() {
return next;
}
public void setNext(Boy next) {
this.next = next;
}
@Override
public String toString() {
return "Boy{" +
"no=" + no +
'}';
}
}
栈(stack)
特点:先入后出
入栈push 出战pop
1.栈的快速入门
1.用数组实现
上代码:
package com.DataStructure._05Stack;
import java.util.Scanner;
/**
* 数组模拟栈!
* 小结:
* 问题:
* 1.遇到一个有意思的问题,scanner的next() nextLine()的问题
* next() 以空格回车tab为结束判断依据
* nextLine() 以回车 为结束
* 如果代码中现有next()后有nextLine(),然后前一个以回车结束,,
* --后面的nextLine()会不执行,但不代表没数据!他的数据就是回车!
*/
public class ArrayStackDemo {
public static void main(String[] args) {
//搞个菜单测试 栈
ArrayStack2 arrayStack = new ArrayStack2(5);
String key = "";
boolean flag = true;
Scanner sc = new Scanner(System.in);
while (flag) {
System.out.println("******** 栈 ********");
System.out.println("--show(s)");
System.out.println("--push(1)");
System.out.println("--pop(2)");
System.out.println("--exit(e)");
System.out.print("--输出选择:");
key = sc.next();
switch (key) {
case "s": {
arrayStack.show();
break;
}
case "1": {
try {
System.out.print("输入添加的数:");
int value = sc.nextInt();
arrayStack.push(value);
break;
} catch (Exception e) {
System.out.println(e);
}
}
case "2": {
try {
int value = arrayStack.pop();
System.out.println(value);
break;
} catch (Exception e) {
System.out.println(e);
}
}
case "e": {
sc.close();
flag = false;
break;
}
default: {
System.out.println("error input!!");
}
break;
}
}
System.out.println("程序退出了!");
}
}
//栈操作
class ArrayStack {
private int maxsize;
private int[] stack;
private int top = -1;
//构造器
public ArrayStack(int maxsize) {
this.maxsize = maxsize;
stack = new int[this.maxsize];
}
//栈空?
boolean isEmpty() {
return top == -1 ? true : false;
}
//栈满?
boolean isFull() {
return top == maxsize - 1 ? true : false;
}
//入栈
public void push(int value) {
if (isFull()) {
throw new RuntimeException("栈满!");
}
top++;
stack[top] = value;
}
//出栈
int pop() {
if (isEmpty()) {
throw new RuntimeException("栈空!");
}
int value = stack[top];
top--;
return value;
}
//遍历
public void show() {
//从栈顶还是遍历
for (int i = top; i >= 0; i--) {
System.out.println(stack[i]);
}
}
}
2.用链表实现栈 (我这里用的尾插法,后来想想头插法效率更高)
上代码:
package com.DataStructure._05Stack;
import org.junit.Test;
import java.util.List;
import java.util.Scanner;
/**
* 栈--单链表实现
*/
public class LinkedListStackDemo {
public static void main(String[] args) {
ListNode listNode1 = new ListNode(1);
ListNode listNode2 = new ListNode(2);
ListNode listNode3 = new ListNode(3);
ListNode listNode4 = new ListNode(4);
ListNode listNode5 = new ListNode(5);
LinkedListStack linkedListStack = new LinkedListStack();
linkedListStack.push(listNode1);
linkedListStack.push(listNode2);
linkedListStack.push(listNode3);
linkedListStack.push(listNode4);
linkedListStack.push(listNode5);
System.out.println("---12345入栈");
linkedListStack.show();
System.out.println("-----54321逐个出栈");
for (int i = 0; i < 5; i++) {
int value = linkedListStack.pop();
System.out.println(value);
}
//linkedListStack.show();
}
}
class LinkedListStack {
ListNode head = new ListNode(0);
public LinkedListStack() {
}
//栈空?栈满?
boolean isEmpty() {
return head.next == null ? true : false;
}
//boolean isFull(){} 链表应该没有栈满的说法
//入栈
public void push(ListNode Node) {
//temp指向最后一个结点 准备尾插法
ListNode temp = head;
while (temp.next != null) {
temp = temp.next;
}
temp.next = Node;
}
//pop出栈--删除结点
int pop() {
if (isEmpty()) {
throw new RuntimeException("栈空!");
}
//temp记录尾结点,tempfer记录temp的前一个结点
ListNode temp = head.next;
ListNode tempfer = head;
while (temp.next != null) {
temp = temp.next;
tempfer = tempfer.next;
}
int no = temp.no;//出栈的值
//删除
tempfer.next = null;
return no;
}
//遍历
public void show() {
if (isEmpty()) {
throw new RuntimeException("栈空!");
}
ListNode temp = head.next;
while (temp.next != null) {
System.out.println(temp);
temp = temp.next;
}
System.out.println(temp);//最后一个
}
}
class ListNode {
int no;
public ListNode next;
public ListNode() {
}
public ListNode(int no) {
this.no = no;
}
@Override
public String toString() {
return "ListNode{" +
"no=" + no +
'}';
}
}
简单计算机(个位数加减乘除)
上代码:
package com.DataStructure._05Stack;
public class CalculatorDemo {
public static void main(String[] args) {
// 3 + 2 * 6 - 2 = ?
String experssion = "3+2*6-2";
//创建两个栈,数栈,符号栈
ArrayStack2 numStack = new ArrayStack2(10);
ArrayStack2 operStack = new ArrayStack2(10);
int index = 0;
int num1 = 0; //
int num2 = 0;
int oper = 0;
int res = 0;
char ch = ' ';
while (true) {
ch = experssion.substring(index, index + 1).charAt(0);
//判断是运算符? 否则是数字
if (operStack.isOper(ch)) {
if (!operStack.isEmpty()) {
//非空,判断优先级
if (operStack.priority(ch) >= operStack.priority(operStack.peek())) {
//优先级比站内优先级低,取出书栈的两个数,和符栈的一个运算符,计算后的结果入数栈;随后该符号ch入栈
num1 = numStack.pop();//先出栈1
num2 = numStack.pop();//后出栈2
oper = operStack.pop();
res = operStack.cal(num1, num2, oper);
//计算的结果入数栈
numStack.push(res);
//该符号ch入符栈
operStack.push(ch);
} else {
//ch的优先级高,直接入栈
operStack.push(ch);
}
} else {
//operStack为空,直接入符栈
operStack.push(ch);
}
} else {
numStack.push(ch - 48);//ascall表个 字符数字比对应的数字大48
}
//判断索引,防止超出字符串长度
index++;
if (index >= experssion.length() ) {
break;
}
}
//剩下的栈 完成计算
while (!operStack.isEmpty()) {
num1 = numStack.pop();//先出栈1
num2 = numStack.pop();//后出栈2
oper = operStack.pop();
res = operStack.cal(num1, num2, oper);
//计算的结果入数栈
numStack.push(res);
}
int result = numStack.pop();
System.out.println(result);
}
}
//创建栈 --这里使用前面创建好的数组栈
//栈操作
class ArrayStack2 {
private int maxsize;
private int[] stack;
private int top = -1;
//构造器
public ArrayStack2(int maxsize) {
this.maxsize = maxsize;
stack = new int[this.maxsize];
}
//栈空?
boolean isEmpty() {
return top == -1;
}
//栈满?
boolean isFull() {
return top == maxsize - 1;
}
//入栈
public void push(int value) {
if (isFull()) {
System.out.println("栈满!");
return;
}
top++;
stack[top] = value;
}
//出栈
int pop() {
if (isEmpty()) {
throw new RuntimeException("栈空~ 没有数据~~");
}
int value = stack[top];
top--;
return value;
}
//遍历
public void show() {
//从栈顶还是遍历
for (int i = top; i >= 0; i--) {
System.out.println(stack[i]);
}
}
//peek看一眼栈顶,但不取出
public int peek() {
return stack[top];
}
//优先级规定,优先级越高,数字越小
public int priority(int oper) {
if (oper == '*' || oper == '/') {
return 1;
} else if (oper == '+' || oper == '-') {
return 2;
} else {
return -1;//假定只有+ - * / 运算
}
}
//判断运算符?数字?
boolean isOper(char val) {
if (val == '+' || val == '-' || val == '*' || val == '/') {
return true;
}
return false;
}
//计算方法
public int cal(int num1, int num2, int oper) {//1先出栈,2后出栈的顺序
int res = 0;
switch (oper) {
case '+': {
res = num1 + num2;
break;
}
case '-': {
res = num2 - num1;
break;
}
case '*': {
res = num1 * num2;
break;
}
case '/': {
res = (num2 / num1);
break;
}
default:
break;
}
return res;
}
}
2.栈 进阶
1.中缀, 后缀, 相互转换
前缀用的不多说了
中缀就是日常人的运算行为: 1+((23+3) * 4)-5
后缀(又称为逆波兰表达式)计算机最喜欢的:1 2 3 + 4 * + 5 -
中缀转后缀,(使用栈) 完成整数计算:
思路
上代码:
package com.DataStructure._05Stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* 逆波兰表达式 ---- 后缀表达式
* <p>
* 中缀转后缀步骤:
*/
/**
* 中缀表达式 转化 后缀表达式
* 说明: 1+((2+3)*4)-5 => 1 2 3 + 4 * + 5 -
*/
public class PolandNotationDemo {
public static void main(String[] args) {
/*
String suffixExperssion = "3 4 + 5 * 6 -";//-->29 空格隔开,方便处理
List<String> list = getListString_suffix(suffixExperssion);
System.out.println("---后缀表达式如下");
System.out.println(list);
System.out.println("---计算结果");
int re = calculator(list);
System.out.println(re);
*/
System.out.println("---------------");
String infixExperssion = "1+((23+3)*4)-5";
List<String> list_infix = getListString_infix(infixExperssion);
System.out.println(infixExperssion + "\n" + "--->中缀表达式转成集合..");
System.out.println(list_infix);
List<String> suffixlist = parseSuffixExpressionList(list_infix);//中转后
System.out.println("------中缀转换后缀表达式,如下....");
System.out.println(suffixlist);
int calculator = calculator(suffixlist);
System.out.println("后缀表达式计算结果---> " + calculator);
}
//中缀表达式放在集合中(遍历字符串,放在集合) 1+((2+3)*4)-5
public static List<String> getListString_infix(String s) {
ArrayList<String> list = new ArrayList<>();
int i = 0;
while (i < s.length()) {
//判断是数字or运算符?
char c = s.charAt(i);
if (c < 48 || c > 57) {//运算符
list.add("" + c); //转成字符串并且加入到list
} else {//数字
//考虑多位数
int j = i + 1;
String cc = "" + c;
while (j < s.length() && s.charAt(j) >= 48 && s.charAt(j) <= 57) {
cc += s.charAt(j);//cc就是多位数字符串
j++;
}
list.add(cc);
i = j - 1;//下次遍历,跳过这个多位数
}
i++;
}
return list;
}
//中缀转成后缀list 1+((2+3)*4)-5 => 1 2 3 + 4 * + 5 -
public static List<String> parseSuffixExpressionList(List<String> ls) {
//两个栈符栈和中间结果栈
//讲解的时候两个栈理解,实操的时候有序中间结果栈没有pop出栈操作,最后还需要倒叙输出,所以换成数组存储
Stack<String> s1 = new Stack<>();
//Stack<String> s2 = new Stack<>();
List<String> s2 = new ArrayList<>();
for (String item : ls) {
if (item.matches("\\d+")) {
//如果是一个数,入s2
s2.add(item);
} else if (item.equals("(")) {
//如果是左括号,入栈s1
s1.push(item);
} else if (item.equals(")")) {
//如果右括号,一次弹出s1栈顶的运算符,压入s2,知道遇到左括号位置,然后这对括号作废
while (true) {
String pop = s1.pop();
if (pop.equals("(")) { //基本的问题 字符串的比较不能用"=="
break;
}
s2.add(pop);
}
} else {
//如果是运算符@
//比较当前的运算符和s1栈顶的优先级
if (s1.size() == 0 || s1.peek().equals("(") ||
OperationPriority.getValue(s1.peek()) > OperationPriority.getValue(item)) {
//1.如果s1为空或者,栈顶是(,则直接将此运算符入栈s1
//2.否则,如果当前优先级高,则入栈s1
s1.push(item);
} else {
// 当前运算符的优先级别 小于或者等于 栈顶运算符
// 否则将s1栈顶的运算符弹出压入s2,再次循环重复运算符的判断@
while (true) {
String pop = s1.pop(); //栈顶元素
s2.add(pop);
if (s1.size() == 0 || s1.peek().equals("(") ||
OperationPriority.getValue(s1.peek()) < OperationPriority.getValue(item)) {//不要想复杂了,直接是重新判断的
break;
}
}
s1.push(item);//经过上面的循环,这里s1要么是空的了,要么是栈顶元素是左括号,要么是优先级问题,就可以当前的item压进去了
}
}
}
//将s1剩余的 依次 压入 s2
if (s1.size() != 0) {
s2.add(s1.pop());
}
return s2;//按照顺序输出就是对应的后缀表达式
}
//后缀表达式装进集合中,方便配合栈
public static List<String> getListString_suffix(String suffixExperssion) {
String[] ss = suffixExperssion.split(" ");
ArrayList<String> list = new ArrayList<>();
for (String value : ss) {
list.add(value);
}
return list;
}
//计算 -- 取后缀表达式集合元素,通过栈处理,
public static int calculator(List<String> ls) {
Stack<String> stack = new Stack<>();
for (String item : ls) {
if (item.matches("\\d+")) {//正则表达式,匹配多位数
//入栈
stack.push(item);
} else {//运算符
//pop两个数字,计算后入栈
int num1 = Integer.parseInt(stack.pop()); //先出
int num2 = Integer.parseInt(stack.pop()); //后出
int res = 0;
if (item.equals("+")) {
res = num1 + num2;
} else if (item.equals("-")) {
res = num2 - num1;
} else if (item.equals("*")) {
res = num2 * num1;
} else if (item.equals("/")) {
res = num2 / num1;//可能有问题,先不做处理了
} else {
throw new RuntimeException("运算符有问题~~~");
}
//res入栈
stack.push("" + res);//新方法 把整数转成字符串
}
}
//最后留在栈的数据就是结果
return Integer.parseInt(stack.pop());
}
}
//判断优先级
class OperationPriority {
public OperationPriority() {
}
public static int getValue(String ch) {
switch (ch) {
case "+":
return 1;
case "-":
return 1;
case "*":
return 2;
case "/":
return 2;
default:
System.out.println("运算符有误!");
}
return 0;
}
}