一、栈
1.括号匹配
public class BracketMatchTest {
public static void main(String[] args) {
String str="上海(长安)())";
final boolean match = isMatch(str);
System.out.println(str+"中的括号是否匹配"+match);
}
/**
* 判断str中的括号是否匹配
* @param str 括号中组成的字符串
* @return 匹配返回true
*/
public static boolean isMatch(String str){
//1.创建站对象,保存左括号
MyStack<String> chars = new MyStack<>();
//2.从左到右遍历字符串
for (int i = 0; i < str.length(); i++) {
String currChar = str.charAt(i)+"";
//3.判断当前字符是否为左括号,如果是,则将字符放到栈中
if(currChar.equals("(")){
chars.push(currChar);
}else if(currChar.equals(")")){
//4.继续判断当前字符是否有括号,如果是则从栈中弹出一个左括号,并判断弹出的结果是否为null,若为null证明没有匹配的左括号。
String pop = chars.pop();
if(pop==null){
return false;
}
}
}
//判断栈中是否存在剩余左括号,若有则证明括号不匹配
if(chars.size()==0){
return true;
}else {
return false;
}
}
}
2. 逆波兰表达式
package baesalgorithm00.part01.SequenceList0.mycaseone;
import java.util.Stack;
//用栈的思想解决逆波兰表达式
public class ReversePolishNotationTest {
public static void main(String[] args) {
//中缀表达式 3*(17-15)+18/6 的逆波兰表达式
String[] notation={"3","17","15","-","*","18","6","/","+"};
int result=caculation(notation);
System.out.println("逆波兰表达式的结果"+result);
}
private static int caculation(String[] notation) {
//定义一个栈存储操作数
Stack<Integer> oprands = new Stack<>();
//从做往右遍历逆波兰表达式,得到每一个元素
for (int i = 0; i < notation.length; i++) {
String curr = notation[i];
//判断当前元素是运算符还是操作数
Integer o1,o2,result;
switch (curr){
//运算符,从栈中弹出两个操作数,完成运算,运算结果压入栈中
case "+":
o1 = oprands.pop();
o2 = oprands.pop();
result=o2+o1;
oprands.push(result);
break;
case "-":
o1 = oprands.pop();
o2 = oprands.pop();
result=o2-o1;
oprands.push(result);
break;
case "*":
o1 = oprands.pop();
o2 = oprands.pop();
result=o2*o1;
oprands.push(result);
break;
case "/":
o1 = oprands.pop();
o2 = oprands.pop();
result=o2/o1;
oprands.push(result);
break;
default:
//操作数,把给操作数压入栈中
oprands.push(Integer.parseInt(curr));
break;
}
}
//得到栈最后一个元素就是逆波兰表达式的结果
return oprands.pop();
}
}
二、链表
快慢指针中的快慢指的是移动的步长,即每次向前移动速度的快慢。例如可以让快指针每次沿链表向前移动2,慢指针每次向前移动1次。
1.快慢指针检测链表中是否有环
public class FastSlowCheckTest {
public static void main(String[] args) {
final Node<String> first = new Node<>("aa", null);
final Node<String> second = new Node<>("bb", null);
final Node<String> third = new Node<>("cc", null);
final Node<String> fourth = new Node<>("dd", null);
final Node<String> fifth = new Node<>("ee", null);
final Node<String> sixth = new Node<>("ff", null);
final Node<String> seventh = new Node<>("gg", null);
//完成结点间指向
first.next=second;
second.next=third;
third.next=fourth;
fourth.next=fifth;
fifth.next=sixth;
sixth.next=seventh;
// 产生环
// seventh.next=third;
//找中间值
boolean circle = isCircle(first);
System.out.println("first链表中是否有环"+circle);
}
/**
* 判断链表中是否有环
* @param first
* @return
*/
public static boolean isCircle(Node<String> first){
//定义快慢指针
Node<String> fast=first;
Node<String> slow=first;
//遍历链表,若快慢指针指向同一个结点,则证明有环
while (fast!=null&&fast.next!=null){
//变换fast和slow
fast=fast.next.next;
slow=slow.next;
if(fast.equals(slow)){
return true;
}
}
return false;
}
//结点类
private static class Node<T>{
//存储数据
T item;
//下一个结点
Node next;
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
}
2.快慢指针查找含环链表入口
public class FastSlowInTest {
public static void main(String[] args) {
final Node<String> first = new Node<>("aa", null);
final Node<String> second = new Node<>("bb", null);
final Node<String> third = new Node<>("cc", null);
final Node<String> fourth = new Node<>("dd", null);
final Node<String> fifth = new Node<>("ee", null);
final Node<String> sixth = new Node<>("ff", null);
// final Node<String> seventh = new Node<>("gg", null);
//完成结点间指向
first.next=second;
second.next=third;
third.next=fourth;
fourth.next=fifth;
fifth.next=sixth;
// sixth.next=seventh;
//产生环
// seventh.next=third;
sixth.next=fourth;
//查找环的入口结点
Node entrance = getEntrance(first);
System.out.println("first链表中环的入口节结点"+entrance.item);
}
/**
* 查找换链表的入口结点
* @param first 链表首结点
* @return 换的入口结点
*/
public static Node getEntrance(Node<String> first){
//定义快慢指针
Node<String> fast=first;
Node<String> slow=first;
Node<String> temp=null;
// 遍历链表,先找到环(快慢指针相遇),准备一个临时指针,指向链表的首结点,继续遍历,直到慢指针和临时指针相遇,那么相遇时所指向的结点就是换的入口
while (fast!=null&&fast.next!=null){
//变换快慢指针
fast=fast.next.next;
slow=slow.next;
//判断指针是否相遇
if (fast.equals(slow)&&temp==null) {
temp=first;
continue;
}
//让临时结点变换
if(temp!=null){
temp=temp.next;
//判断临时指针是否和慢指针相遇
if(temp.equals(slow)|| temp.equals((fast))){
break;
}
}
}
return temp;
}
//结点类
private static class Node<T>{
//存储数据
T item;
//下一个结点
Node next;
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
}
3.快慢指针查找中间值
public class FastSlowInTest {
public static void main(String[] args) {
final Node<String> first = new Node<>("aa", null);
final Node<String> second = new Node<>("bb", null);
final Node<String> third = new Node<>("cc", null);
final Node<String> fourth = new Node<>("dd", null);
final Node<String> fifth = new Node<>("ee", null);
final Node<String> sixth = new Node<>("ff", null);
// final Node<String> seventh = new Node<>("gg", null);
//完成结点间指向
first.next=second;
second.next=third;
third.next=fourth;
fourth.next=fifth;
fifth.next=sixth;
// sixth.next=seventh;
//产生环
// seventh.next=third;
sixth.next=fourth;
//查找环的入口结点
Node entrance = getEntrance(first);
System.out.println("first链表中环的入口节结点"+entrance.item);
}
/**
* 查找换链表的入口结点
* @param first 链表首结点
* @return 换的入口结点
*/
public static Node getEntrance(Node<String> first){
//定义快慢指针
Node<String> fast=first;
Node<String> slow=first;
Node<String> temp=null;
// 遍历链表,先找到环(快慢指针相遇),准备一个临时指针,指向链表的首结点,继续遍历,直到慢指针和临时指针相遇,那么相遇时所指向的结点就是换的入口
while (fast!=null&&fast.next!=null){
//变换快慢指针
fast=fast.next.next;
slow=slow.next;
//判断指针是否相遇
if (fast.equals(slow)&&temp==null) {
temp=first;
continue;
}
//让临时结点变换
if(temp!=null){
temp=temp.next;
//判断临时指针是否和慢指针相遇
if(temp.equals(slow)|| temp.equals((fast))){
break;
}
}
}
return temp;
}
//结点类
private static class Node<T>{
//存储数据
T item;
//下一个结点
Node next;
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
}
4.链表反转
public class MyLinkedListOne<T> implements Iterable<T>{
//记录头结点
private Node head;
//记录链表长度
private int N;
//结点类
private class Node{
//存储数据
T item;
//下一个结点
Node next;
public Node(T item,Node next){
this.item=item;
this.next=next;
}
}
public MyLinkedListOne() {
//初始化头结点
this.head=new Node(null,null);
//初始化元素个数
this.N=0;
}
//清空链表
public void clear(){
this.head.next=null;
this.N=0;
}
//获取链表长度
public int length(){
return this.N;
}
//判断链表是否为空
public boolean isEmpty(){
return this.N==0;
}
//获取指定位置i处的元素
public T get(int i){
//通过循环从头结点开始向后找i此即可得到目标
Node n=this.head.next;
for (int index = 0; index < i; index++) {
n=n.next;
}
return n.item;
}
//向链表中添加元素T
public void insert(T t){
//找到当前最后一个结点
Node n=this.head;
while (n.next!=null){
n=n.next;
}
//创建新结点,保存元素T
Node newNode = new Node(t, null);
//让当前最后一个结点指向新节点
n.next=newNode;
//元素个数+1
this.N++;
}
//向指定位置i处添加元素T
private void insert(int i,T t){
//找到i位置的前一个结点
Node pre=this.head;
for (int index = 0; index < i; index++) {
pre=pre.next;
}
//找到i位置结点
Node curr=pre.next;
//创建新结点,并且新结点需要指向原来i位置的结点
final Node newNode = new Node(t, curr);
//原来i位置的前一个结点指向新结点
pre.next=newNode;
//元素个数+1
this.N++;
}
//删除指定位置i处的元素,并返回被删除的元素
public T remove(int i){
//找到i位置的前一个结点
Node pre = this.head;
for (int index = 0; index < i; index++) {
pre=pre.next;
}
//找到i位置结点
Node nowNode = pre.next;
//找到i位置下一个结点
Node curr = nowNode.next;
//前一个结点指向下一个结点
pre.next=curr;
//元素个数减一
this.N--;
return nowNode.item;
}
//查找元素T在链表中第一次出现的位置
public int indexOf(T t){
//从头结点开始遍历,取出每一个item进行比较,若相同则找到了
Node n=head;
for (int i = 0; n.next!=null; i++) {
n=n.next;
if(n.item.equals(t)){
return i;
}
}
return -1;
}
@Override
public Iterator<T> iterator() {
return new LIterator();
}
private class LIterator implements Iterator{
private Node n;
public LIterator(){
this.n=head;
}
@Override
public boolean hasNext() {
return n.next!=null;
}
@Override
public Object next() {
n=n.next;
return n.item;
}
}
//用于反转整个链表
public void reverse(){
//判断当前链表是否为空链表,若是空链表,则结束运行,若不是,则调用重载的reverse方法完成反转
if(isEmpty()){
return;
}
reverse(head.next);
}
//反转指定的结点curr,并把反转后的结点返回
public Node reverse(Node curr){
if(curr.next==null){
head.next=curr;
return curr;
}
//递归反转当前结点curr的下一个结点;返回值就是链表反转后,当前结点的上一个结点
Node pre = reverse(curr.next);
//让返回的下一个结点变为当前结点curr
pre.next=curr;
//把当前结点的下一个结点变为null
curr.next=null;
return curr;
}
}
三、双向链表
1.约瑟夫问题:
题目背景:
据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决。Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。
问题分析与算法设计
约瑟夫问题并不难,但求解的方法很多;题目的变化形式也很多。这里给出一种实现方法。
题目中30个人围成一圈,因而启发我们用一个循环的链来表示,可以使用结构数组来构成一个循环链。结构中有两个成员,其一为指向下一个人的指针,以构成环形的链;其二为该人是否被扔下海的标记,为1表示还在船上。从第一个人开始对还未扔下海的人进行计数,每数到9时,将结构中的标记改为0,表示该人已被扔下海了。这样循环计数直到有15个人被扔下海为止。(摘录自百度)
代码实现
package baesalgorithm00.part01.SequenceList0.mycaseone;
//约瑟夫问题:一群带编号的人围成的圈中轮流报数,报道3的倍数则退出圈,问最后剩下几号人
public class josepTest {
private static class Node<T>{
T item;
Node next;
public Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
public static void main(String[] args) {
//解决约瑟夫问题
//1.构建循环链表,包含41个结点,分别存储1-41之间的数字
//记录首结点
Node<Integer> first=null;
//用于记录前一个结点
Node<Integer> pre=null;
for (int i = 1; i < 42; i++) {
//若是第一个结点
if(i==1){
first=new Node<>(i,null);
pre=first;
continue;
}
//若不是第一个结点
Node<Integer> newNode = new Node<>(i, null);
pre.next=newNode;
pre=newNode;
//若是最后一个结点,则需要让最后一个结点的下一个结点指向首结点
if(i==41){
pre.next=first;
}
}
//2.需要count计数器,模拟报数
int count=0;
//3.循环链表
//记录每次遍历拿到的结点,默认从首结点开始
Node<Integer> n=first;
//记录当前结点的上一个结点
Node<Integer> before=null;
while (n!=n.next){
//模拟报数
count++;
//判断当前报数是不是为3
if(count==3){//如果是3,则吧当前结点删除调用,打印点前结点,重置count=0,让当前结点n后移
before.next=n.next;
System.out.print(n.item+",");
count=0;
n=n.next;
}else {//若不是3,让before变为当前结点,让当前结点后移
before=n;
n=n.next;
}
}
//打印最后一个元素
System.out.println(n.item);
}
}