java 环形单链表实现及约瑟夫问题求解

环形单链表其实跟普通单链表相识,只是最后一个元素会指向第一个元素;而普通的单链表最后的一个元素只是指向 null.

不过在实现的时候需要注意一下这个环形链表的遍历的退出条件,以及在插入与删除元素的时候,怎么去操作其中的节点。(与普通单链表会存在细微差别。)

然后,简单介绍一下约瑟夫问题:

问题描述
约瑟夫环问题(Joseph)又称丢手绢问题:已知 m 个人围坐成一圈,由某人起头,下一个人开始从 1 递增报数,报到数字 n 的那个人出列,他的下一个人又从 1 开始报数,数到 n 的那个人又出列;依此规律重复下去,直到 m 个人全部出列约瑟夫环结束。如果从 0 ~ (m-1) 给这 m 个人编号,请输出这 m 个人的出列顺序。
————————————————
版权声明:本文为CSDN博主「李春春_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhongkelee/article/details/40594147

这个问题描述看起来清楚明了。直接在纸上画能很容易的求解。但是如果 n,很大的话,就不好弄了。所以,需要一个通用的解决方案。

刚好环形链表可以解决这个问题。把这 n个人当成一个环形链表,然后就是根据 k, m 找到对应的位置,去删除对应的元素,依次反复,直到最后一个元素被删除。

这里直接给出实现:

为了便于 插入与删除。定义 head 和 tail, 固定指向链表的头结点和尾结点。


import java.util.Arrays;
import java.util.StringJoiner;

/**
 * 环形单链表的实现.
 */
class AroundList<Z> {

    private static class Node<K> {
        K data;
        Node<K> next;
    }

    private final Node<Z> head = new Node<>();
    private final Node<Z> tail = new Node<>();

    private int length = 0;

    public AroundList() {
        head.data = null;
        head.next = null;
    }

    public int size() {
        return length;
    }

    public boolean isEmpty() {
        return length == 0;
    }

    public boolean add(Z data) {
        return insert(0, data);
    }

    public boolean addLast(Z data) {
        return insert(length, data);
    }

    public boolean replace(int index, Z data) {
        if (index < 0 || index >= length) {
            return false;
        }
        int pos = -1;
        Node<Z> h = head;
        do {
            if (index - 1 == pos) {
                break;
            }
            pos++;
            h = h.next;
        } while (h.next != head.next);

        h.next.data = data;
        return true;
    }

    // a [k] b c d
    public boolean insert(int index, Z data) {
        if (index < 0 || index > length) {
            return false;
        }
        if (length == 0) {
            Node<Z> young = new Node<>();
            young.data = data;
            young.next = young;
            head.next = young;
            tail.next = young;
        } else {
            int pos = -1;
            Node<Z> h = head;
            do {
                if (index - 1 == pos) {
                    break;
                }
                pos++;
                h = h.next;
            } while (h.next != head.next);

            if (pos == length) {
                return false;
            }
            Node<Z> old = h.next;
            Node<Z> young = new Node<>();
            young.data = data;
            young.next = old;
            h.next = young;
            if (pos == -1) {
                // let tail.next == young
                tail.next.next = young;
            } else if (pos == length - 1) {
                tail.next = young;
            }
        }
        length++;

        return true;
    }

    public int indexOf(Z data) {
        int pos = -1;
        if (data == null) {
            return pos;
        }
        Node<Z> h = head;
        boolean found = false;
        do {
            pos++;
            h = h.next;
            if (data.equals(h.data)) {
                found = true;
                break;
            }
        } while (h.next != head.next);
        return found ? pos : -1;
    }

    public Z get(int index) {
        if (index < 0 || index >= length) {
            return null;
        }
        int pos = -1;
        Node<Z> h = head;
        do {
            if (index - 1 == pos) {
                break;
            }
            pos++;
            h = h.next;
        } while (h.next != head.next);
        return h.next.data;
    }

    public boolean remove(Z data) {
        return remove(indexOf(data));
    }

    public boolean removeLast() {
        return remove(length - 1);
    }

    public boolean removeFirst() {
        return remove(0);
    }

    public boolean remove(int index) {
        if (index < 0 || index >= length) {
            return false;
        }
        int pos = -1;
        Node<Z> h = head;
        do {
            if (index - 1 == pos) {
                break;
            }
            pos++;
            h = h.next;
        } while (h.next != head.next);

        if (pos == length) {
            return false;
        }
        // now h' pos == index-1
        Node<Z> target = h.next;
        h.next.data = null; // remove target
        h.next = target.next;
        if (pos == -1) {
            // remove the first ele
            tail.next.next = h.next;
        } else if (pos == length - 2) {
            // remove the last ele
            tail.next = h;
        }
        length--;
        return true;
    }

    public boolean clear() {
        while (length > 0) {
            removeFirst();
        }
        return true;
    }

    public void listReverse() {
        // _ 3 8 9 7 6
        // + 8 3
        if (length <= 1) {
            return;
        }
        Node<Z> rh = new Node<>();

        Node<Z> next;
        Node<Z> tt = null;
        Node<Z> h = head.next;
        do {
            next = h.next;
            h.next = rh.next;
            rh.next = h;
            if (tt == null) {
                tt = h;
            }
            h = next;
        } while (h != head.next);
        tail.next = tt;
        tail.next.next = rh.next;
        head.next = rh.next;
    }

    /**
     * @param order 0== no sort; 1== 3,2,1 , -1 == 1,2,3.
     */
    public <T extends Comparable<T>> AroundList<T> merge(AroundList<T> another, int order) {
        AroundList<T> list = new AroundList<>();
        if (0 == order) {
            for (int i = 0; i < size(); ++i) {
                list.addLast((T) get(i));
            }
            if (another != null) {
                for (int i = 0; i < another.size(); ++i) {
                    list.addLast(another.get(i));
                }
            }
        } else {
            for (int i = 0; i < size(); ++i) {
//                list.addLast((T) get(i));
                T data = (T) get(i);
                int j;
                for (j = 0; j < list.size(); ++j) {
                    T z = list.get(j);
                    if (order < 0
                            ? z.compareTo(data) > 0 :
                            z.compareTo(data) < 0) {
                        break;
                    }
                }
                list.insert(j, data);
            }
            if (another != null) {
                for (int i = 0; i < another.size(); ++i) {
                    T data = another.get(i);
                    int j;
                    for (j = 0; j < list.size(); ++j) {
                        T z = list.get(j);
                        if (order < 0
                                ? z.compareTo(data) > 0 :
                                z.compareTo(data) < 0) {
                            break;
                        }
                    }
                    list.insert(j, data);
                }
            }
        }
        return list;
    }

    public String format() {
        int pos = -1;
        StringBuilder sb = new StringBuilder(getClass().getSimpleName() + ": {");
        if (length > 0) {
            Node<Z> h = head;
            do {
                h = h.next;
                pos++;
                sb.append("[").append(pos).append("]=").append(h.data).append(", ");
            } while (h.next != head.next);
        }
        int last = sb.lastIndexOf(", ");
        if (last != -1) {
            sb.delete(last, last + 2);
        }
        sb.append("} | size=").append(length).append(" ##|");
        return sb.toString();
    }

    @Override
    public String toString() {
        return new StringJoiner(", ",
                AroundList.class.getSimpleName() + "[", "]")
                .add(format()).toString();
    }

    public static void main(String[] args) {
        joseph(5, 1, 2);
        System.out.println("@@@@");
        joseph(5, 3, 2);
//        if (1 == 1) return;
        AroundList<Integer> list = new AroundList<>();
        System.out.println(list);
        list.add(1);
        System.out.println(list);
        list.removeFirst();
        System.out.println("--rr-" + list);
//        if (1 == 1) return;
        list.add(2);
        list.add(3);

        System.out.println("#######" + list);
        list.listReverse();
        System.out.println("r-r-r-r? " + list);
        list.addLast(4);
        list.addLast(5);
        list.addLast(6);
        list.add(-1);
        System.out.println("add... " + list);
        list.insert(3, 44);
        System.out.println("is: " + list);
        list.remove(list.size() - 1);
        System.out.println("rs-1?" + list);
        list.clear();
        System.out.println("clear---------" + list);
        list.add(12);
        System.out.println("----??? " + list);
        list.add(13);
        list.addLast(14);
        System.out.println(list);
        list.remove(0);
        System.out.println("r0? " + list);
        list.remove(1);
        System.out.println("r1? " + list);
        list.replace(0, -11);
        list.replace(1, -22);
        System.out.println("rp? " + list);
        for (int i = 0; i < list.size(); ++i) {
            Integer value = list.get(i);
            System.out.println(" get=-- [" + i + "] = " + value);
            System.out.println("index of " + list.indexOf(value));
        }
        System.out.println(list.indexOf(Integer.MIN_VALUE));
        System.out.println(list.indexOf(Integer.MAX_VALUE));

        joseph(6, 2, 4);
    }

    private static void joseph(int count, int start, int times) {
        if (start > count || times > count
                || count <= 0 || start <= 0 || times <= 0) {
            return;
        }
        // 5, 1, 2
        AroundList<Integer> list = new AroundList<>();
        for (int i = 0; i < count; ++i) {
            list.addLast(i + 1);
        }
        int[] results = new int[count];
        int offset = -99;
        for (int i = 0; i < count; ++i) {
            int size = list.size();
            int begin = i == 0 ? start - 1 : offset;
            offset = (begin + times - 1) % size;
            Integer value = list.get(offset);
            boolean rm = list.remove(offset);
            // System.out.printf("b=%d,offset=[%d], value=%d, rm=%s\n", begin, offset, value, rm);
            results[i] = value;
        }
        System.out.println("results: " + Arrays.toString(results));
        System.out.println("list--" + list);
    }
}

这里需要关注 insert(index, data)remove(index, data) 的逻辑。环形链表的问题在于,最后一个元素始终会指向第一个元素,所以在 插入到链表头,及插入到链表尾;删除头元素,删除尾元素 的时候需要特别注意。

然后是约瑟夫问题的解决:

    private static void joseph(int count, int start, int times) {
        if (start > count || times > count
                || count <= 0 || start <= 0 || times <= 0) {
            return;
        }
        // 5, 1, 2
        AroundList<Integer> list = new AroundList<>();
        for (int i = 0; i < count; ++i) {
            list.addLast(i + 1);
        }
        int[] results = new int[count];
        int offset = -99;
        for (int i = 0; i < count; ++i) {
            int size = list.size();
            int begin = i == 0 ? start - 1 : offset;
            offset = (begin + times - 1) % size;
            Integer value = list.get(offset);
            boolean rm = list.remove(offset);
            // System.out.printf("b=%d,offset=[%d], value=%d, rm=%s\n", begin, offset, value, rm);
            results[i] = value;
        }
        System.out.println("results: " + Arrays.toString(results));
        System.out.println("list--" + list);
    }

在已经实现了环形链表的前提下,约瑟夫问题就变成了找到要删除的元素的位置,然后删除该元素。 不过要注意,找这个元素的逻辑。就是怎么去计算对应的索引值的偏移量。

看一下输出结果:

results: [2, 4, 1, 5, 3]
list--AroundList[AroundList: {} | size=0 ##|]
@@@@
results: [1, 5, 2, 4, 3]
...

可以看到,如果是5个人,从第1个开始数数,每数2个,就踢出;那么输出的顺序是2, 4, 1, 5, 3; 如果是5个人,从第3个开始数数,每数4个,就踢出;那么输出的顺序是1, 5, 2, 4, 3

数的时候要包含当前的位置,自己也要被数一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值