基本数据结构与算法JavaAPI【1】--线性表拓展篇

一、栈

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);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值