第九章 列表,链表,栈,队列

1、桶排序

 工作的原理是将数组分到有限数量的桶子里。<br />
 * 每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。<br />
 * 桶排序是鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))。<br />
 * 但桶排序并不是 比较排序,他不受到 O(n log n) 下限的影响。<br />
 *
 * 时间复杂度: O(N+C),其中C=N*(logN-logM)<br />
 * 空间复杂度:N+M,M为桶的个数<br />
 * 非原址排序<br />
 * 稳定性:稳定<br />
 *
 * 桶排序假设数据会均匀入桶,在这个前提下,桶排序很快!

tip:鸽巢排序:https://blog.csdn.net/u010647471/article/details/49498097

需要在意的是每个节点都是node,而且要往每个node后面添node,就要写成链表的格式,然后爱意一下node的定义,都是用public,所以就不需要去写get,set方法,Linkednode next就是下一个节点的意思,又一篇blog在链表这里写的很好https://www.cnblogs.com/whgk/p/6589920.html

public class LinkedNode {
    public int value;
    public LinkedNode next;

    public LinkedNode(int value){
        this.value = value;
    }
}
class Test48{
    public static void main(String[] args) {
        int arr[] = {1,7,8,9,3,6,5,4,2,3};
        sort(arr);
        System.out.println(arr.toString());

    }
    public static void sort(int[] arr){
        int length= arr.length;
        LinkedNode[] bucket =new LinkedNode[length]; //通的个数!!看好这里是怎么记录那个链的,关于LinkedNode的数组
        int max=0;
        for(int i =0;i<length;i++){
            if(arr[i]>max){
                max = arr[i];
            }
        }
        //入桶
        for(int i=0;i<length;i++){
            int value = arr[i];
            int hash = hash(arr[i],max,length);
            if(bucket[hash]==null){
                bucket[hash] = new LinkedNode(value);

            }else {
                insertnode(value,bucket[hash],bucket,hash);//这里的这个insert不是往最后,而是往后找知道找到一个比他大的,放到他的前面
            }
        }

        //出桶
        int k = 0;
        for(LinkedNode node:bucket){
            if(node!=null){
                while (node!=null){
                    arr[k++]  = node.value; //因为是桶排序,所以这里直接用同种东西替换原来数组
                    node = node.next;
                }
            }
        }
    }
    public static int hash(int ele,int max,int length){
        return  (ele*length)/(max+1);
    }
    public static void insertnode(int value,LinkedNode head,LinkedNode[] bucket,int hash){
        LinkedNode newnode =  new LinkedNode(value);
        //小于头结点就放在头上
        if(value<head.value){
            newnode.next = head;
            bucket[hash] = newnode;
            return; //这里return就不用再往下面找了,方便
        }
        //往后找第一个比当前值大的节点,放在他前面
        LinkedNode  p  =head; //不放在头部就不要改动头部节点,复制一下 //p是后面的大节点
        LinkedNode pre=  p;//前面的节点也先跟后面的节点一样//pre是前面的小节点
        while (p!=null&& value>p.value){
            pre = p;
            p = p.next;
        }
        if(p==null ){//跑到末尾了
            pre.next = newnode;
        }else {//插入pre和p之间
            pre.next = newnode;
            newnode.next = p;
        }
    }
}

2、猫狗收容所

/**有家动物收容所只收留猫和狗,但有特殊的收养规则,收养人有两种收养方式,
 第一种为直接收养所有动物中最早进入收容所的,
 第二种为选择收养的动物类型(猫或狗),并收养该种动物中最早进入收容所的。

 给定一个操作序列int[][2] ope(C++中为vector<vector<int>>)代表所有事件。
 若第一个元素为1,则代表有动物进入收容所,第二个元素为动物的编号,正数代表狗,负数代表猫;
 若第一个元素为2,则代表有人收养动物,第二个元素若为0,则采取第一种收养方式(最早),
 若为1,则指定收养狗,若为-1则指定收养猫。
 请按顺序返回收养的序列。若出现不合法的操作,即没有可以符合领养要求的动物,则将这次领养操作忽略。
 测试样例:

 [[1,1],[1,-1],[2,0],[2,-1]]

 返回:[1,-1]

思路:用队列,先进来的先出去

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;

class Kruskal{
    public static void main(String[] args) {
        Kruskal obj = new Kruskal();
        int[][] data = {{1, 1}, {1, -1}, {1, 0}, {1, -5}, {2, 0}, {2, -1}, {2, 0}};
        System.out.println(obj.asylum(data));
    }
    static int timeline;
    private static class Animal{
        int type;
        int time;
       

        public Animal(int type){  //每new一个动物出来,time自动就加一个一,,timeline又是全局变量
            this.type = type;
            this.time = timeline++;

        }
    }
    static ArrayList<Integer> asylum(int[][] ope){
        ArrayList<Integer> res = new ArrayList<>();

        Queue<Animal> cats = new LinkedList<>();
        Queue<Animal> dogs = new LinkedList<>();
        for(int[] row :ope){
            int op = row[0];
            int typenumber = row[1];
            if(op ==1){
                if(typenumber>0){
                    dogs.add(new Animal(typenumber));
                }
                if(typenumber<0){
                    cats.add(new Animal(typenumber));
                }
            }else if(op ==2){
                if(typenumber == 0) {
                    if((!dogs.isEmpty())&&(cats.isEmpty() || dogs.peek().time<cats.peek().time)){
                        res.add(dogs.poll().type);
                    }
                    if ((!cats.isEmpty()) && (dogs.isEmpty() || dogs.peek().time > cats.peek().time)) {
                        res.add(cats.poll().type);
                    }else {
                        if (typenumber == 1 && !dogs.isEmpty()) {
                            res.add(dogs.poll().type);
                        }
                        if (typenumber == -1 && !cats.isEmpty()) {
                            res.add(cats.poll().type);
                        }
                    }
                }
            }
        }
        return res;
    }

}

3、删掉链表重复的节点,但是这里有一个bug,因为他只能删除一次,如果案例有三个6,删完就还剩两个6

import java.util.HashSet;

class Test48{

    public static void main(String[] args) {
        int []data  = {1,6,7,3,6};
        Node head = new Node(null);//head固定死
        Node p = head;//从这个节点开始遍历
        for(int i=0;i<data.length;i++){
            p.next = new Node(data[i]);
            p = p.next;
        }
        rr(head);
        //遍历一下,遍历的是链表
        Node p1 =head.next;
        while (p1!=null){
            System.out.println(p1.value);
            p1 = p1.next;
        }


    }
    public static void rr(Node head){
        HashSet set = new HashSet();
        Node pre = head;
        Node p1 = head.next;
        while (p1!=null){
            if(set.contains(p1.value)){
                pre.next = p1.next;
            }else {
                set.add(p1.value);
            }
            pre = p1;
            p1 = p1.next;//要两个指针的原因就是删除的话可以前面的next等于后面的next
        }
    }
    private static class Node{
        public Object value;//object因为不知道value什么类型的
        public Node next;

        public Node(Object value){
            this.value = value;
        }
    }
}

改进一下:一行代码的问题:

import java.util.HashSet;

class Test48{

    public static void main(String[] args) {
        int []data  = {1,6,7,3,6,6};
        Node head = new Node(null);//head固定死
        Node p = head;//从这个节点开始遍历
        for(int i=0;i<data.length;i++){
            p.next = new Node(data[i]);
            p = p.next;
        }
        rr(head);
        //遍历一下,遍历的是链表
        Node p1 =head.next;
        while (p1!=null){
            System.out.println(p1.value);
            p1 = p1.next;
        }


    }
    public static void rr(Node head){
        HashSet set = new HashSet();
        Node pre = head;
        Node p1 = head.next;
        while (p1!=null){
            if(set.contains(p1.value)){
                pre.next = p1.next;
            }else {
                set.add(p1.value);
                pre = p1;
            }

            p1 = p1.next;//要两个指针的原因就是删除的话可以前面的next等于后面的next
        }
    }
    private static class Node{
        public Object value;//object因为不知道value什么类型的
        public Node next;

        public Node(Object value){
            this.value = value;
        }
    }
}

4、找出单向链表中倒数第k个元素

思路:第一个指针一直加加加叫到k,第二个指针从头开始,然后两个指针一起向后挪

class Test48{
    public static void main(String[] args) {
        int []arr  = {1,6,7,3,6,6};
        Listnode head = new Listnode(null);
        Listnode p = head;
        for(int i=1;i<arr.length;i++){
            p.next = new Listnode(arr[0]);
            p = p.next;
        }
        kthnode exam  = new kthnode();
        Listnode l1 = exam.findkthtotail(head,4);
        System.out.println(l1.value);
        System.out.println("+++++" + exam.findkthtotail(head, 3).value);
    }

}
class Listnode{
    public Object value;
    public Listnode next;

    Listnode(Object value){
        this.value = value;
    }
    public String toString(){
        StringBuilder sb =  new StringBuilder(value+" ");
        Listnode nnext = next;
        while (nnext !=null){
            sb.append(nnext.value);
            nnext  = nnext.next;
        }
        return sb.toString();
    }
}

class kthnode{
    public Listnode findkthtotail(Listnode head,int k){
        if(head ==null || k<0){
            return null;
        }
        Listnode p1  = head;
        Listnode p2 = head;
        int count=  0;
        while (p2!=null &&count<k){
            p2  =p2.next;
            count++;
        }
        if(count<k){
            return null;
        }
        while (p2!=null){
            p1=p1.next;
            p2 = p2.next;
        }
        return p1;

    }
}

这里代码有点问题,一直返回都是1,,返现问题后改过来好了

5、删除单项链表的某节点

* 实现一个算法,删除单向链表中间的某个结点,假定你只能访问该结点。
 * 示例:
 * 输入单向链表a->b->c->d->e中的节点c
 * 结果:不返回任何数据,但该链表变为a->b->d->e
 *
 给定待删除的节点,请执行删除操作,若该节点为尾节点,返回false,否则返回true

public class _2_3RemoveNodeN1 {
  public boolean removeNode(ListNode pNode) {
    if (pNode.next == null)
      return false;
    pNode.val = pNode.next.val;//复制后继的内容
    pNode.next = pNode.next.next;//跨越后继
    return true;
  }
}

6、用基准值将链表区分

 * 编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前
 给定一个链表的头指针 ListNode* pHead,请返回重新排列后的链表的头指针。

 注意:分割以后保持原来的数据顺序不变。

 不要开辟新的空间,即不要新建节点

public class _2_4PartitionLinkNode {

  public ListNode partition(ListNode pHead, int x) {
    ListNode p = pHead;
    ListNode leftFirst = null;
    ListNode leftTail = null;
    ListNode rightFirst = null;
    ListNode rightTail = null;

    while (p != null) {//顺序扫描所有节点
      int pValue = p.val;
      if (pValue < x) {//小于x
        if (leftTail == null) {
          leftFirst = p;
          leftTail = p;
        } else {
          leftTail.next = p;
          leftTail = leftTail.next;
        }
      } else {//大于等于x
        if (rightTail == null) {
          rightFirst = p;
          rightTail = p;
        } else {
          rightTail.next = p;
          rightTail = rightTail.next;
        }
      }
      p = p.next;
    }
    if (leftFirst == null) {// 左边链表可能为空
      return rightFirst;
    }
    leftTail.next = rightFirst;//左右两个链表连接起来
    if (rightTail != null)
      rightTail.next = null;
    return leftFirst;
  }
}

7、链表逐位相加

/*有两个用链表表示的整数,每个结点包含一个数位。
这些数位是反向存放的,也就是个位排在链表的首部。编写函数对这两个整数求和,并用链表形式返回结果。
给定两个链表ListNode* A,ListNode* B,请返回A+B的结果(ListNode*)。
测试样例:
{1,2,3},{3,2,1}
返回:{4,4,4}

{7,4,0,7,5},{2,7,2,3,4}
返回:{9,1,3,0,0,1}

思路: 这里一定要用listnode的string方法我不太清楚,但是如果去掉string方法,输出则会变成Listnode@77459877
Listnode@5b2133b1
Listnode@72ea2f77

不太懂。。。。

class Test48{
    public static void main(String[] args){
        Listnode node1 = new Listnode(7);
        node1.next = new Listnode(4);
        node1.next.next = new Listnode(0);
        node1.next.next.next = new Listnode(7);
        node1.next.next.next.next = new Listnode(5);
        System.out.println(node1);

        Listnode node2 = new Listnode(2);
        node2.next = new Listnode(7);
        node2.next.next = new Listnode(2);
        node2.next.next.next = new Listnode(3);
        node2.next.next.next.next = new Listnode(4);
        System.out.println(node2);

        Listnode result  =  plusAB(node1,node2,0);
        System.out.println(result);

    }
    static Listnode plusAB(Listnode a,Listnode b,int i){
        if(a==null && b ==  null && i==0){
            return null;
        }
        int value = i;
        if(a!=null){
            value += a.val;
        }
        if(b!=null){
            value+= b.val;
        }
        Listnode result  =new Listnode(value%10);
        result.next = plusAB(a==null?null:a.next,b==null?null:b.next,value>=10?1:0);//这里如果直接写a.next和b.next会报越界额错误

        return result;
    }

}
class Listnode{
    public int val;
    public Listnode next;

    Listnode(int value){
        this.val = value;
    }
    public String toString(){
        StringBuilder sb =  new StringBuilder(val+" ");
        Listnode nnext = next;
        while (nnext !=null){
            sb.append(nnext.val);
            nnext  = nnext.next;
        }
        return sb.toString();
    }
}

这道题有一个变形,如果变成了开头是高位,后面是地位的话,而且两个链表长短不一样,那又该怎么办呢?

public class _2_5PlusLinkNode {
  public ListNode plusAB(ListNode a, ListNode b) {
    return plusAB(a, b, 0);
  }
  private ListNode plusAB(ListNode a, ListNode b, int i) {
    if (a == null && b == null && i == 0)
      return null;
    int value = i;
    if (a != null)
      value += a.val;
    if (b != null)
      value += b.val;
    ListNode result = new ListNode(value % 10);
    result.next = plusAB(a == null ? null : a.next,
        b == null ? null : b.next,
        value >= 10 ? 1 : 0);
    return result;
  }

  private class NodeAndValue {
    ListNode node;
    boolean flag;

    public NodeAndValue(ListNode node, boolean flag) {
      this.node = node;
      this.flag = flag;
    }

    @Override
    public String toString() {
      return "NodeAndValue{" +
          "node=" + (flag ? "1" : "") + node.toString() +
          ", flag=" + flag +
          '}';
    }
  }

  //此处不考虑a,b长度不一样
  //如果不一样,先补齐
  private NodeAndValue plusAB1(ListNode a, ListNode b) {
    if (a.next == null && b.next == null) {
      int v = a.val + b.val;
      return new NodeAndValue(new ListNode(v % 10), v >= 10);
    }

    NodeAndValue other = plusAB1(a.next, b.next);
    int v;
    if (other.flag) {
      v = a.val + b.val + 1;
    } else {
      v = a.val + b.val;
    }
    if (v >= 10) {
      v = v % 10;
      ListNode res = new ListNode(v);
      res.next = other.node;
      return new NodeAndValue(res, true);
    } else {
      ListNode res = new ListNode(v);
      res.next = other.node;
      return new NodeAndValue(res, false);
    }
  }

  public static void main(String[] args) {
    _2_5PlusLinkNode obj = new _2_5PlusLinkNode();

    ListNode node1 = new ListNode(7);
    node1.next = new ListNode(4);
    node1.next.next = new ListNode(0);	n
    node1.next.next.next = new ListNode(7);
    node1.next.next.next.next = new ListNode(5);
    System.out.println(node1);

    ListNode node2 = new ListNode(2);
    node2.next = new ListNode(7);
    node2.next.next = new ListNode(2);
    node2.next.next.next = new ListNode(3);
    node2.next.next.next.next = new ListNode(4);
    System.out.println(node2);

    ListNode result = obj.plusAB(node1, node2);
    System.out.println(result);
    NodeAndValue res = obj.plusAB1(node1, node2);
    System.out.println(res);

    Assertions.assertThat(result.toString()).isEqualTo("913001");
  }
}

8、判断回文链表

这个就是一个我觉得十分难判断的边界问题,,先要清楚的是odd表示的是奇数,even表示的是偶数

import java.util.Stack;

class test3{
    public static void main(String[] args) {
        Listnode node = new Listnode(1);
        node.next  = new Listnode(2);
        node.next.next = new Listnode( 32 );
        node.next.next.next = new Listnode( 32 );
        node.next.next.next.next = new Listnode( 2 );
        node.next.next.next.next.next = new Listnode( 1 );
        System.out.println(isPalindrome(node));
    }
    static boolean isPalindrome(Listnode pHead){
        if(pHead ==null){
            return false;
        }
        if(pHead.next ==null ){
            return true;
        }
        Listnode slower = pHead;
        Listnode faster  = pHead;
        Stack<Listnode> stack =  new Stack<>();
        boolean isodd = true;
        while (faster!=null && faster.next!=null){//这儿不要直接去遍历找中间节点,,不然就没意思了
            stack.push(slower);
            slower = slower.next;
            faster = faster.next.next;
            if(faster == null){//faster为null,就是偶数个,slower指针不用动位置
                isodd  = false;
            }
        }
        if(isodd){ //如果是奇数的话,slower再往后面走一个
            slower = slower.next;
        }
        while (!stack.empty()){
            if(stack.pop().val !=slower.val){ //pop 直接弹出,而peek是摸一下最上面的
                return false;
            }else {
                    slower = slower.next;
            }
        }

        return true;
    }
}

猜你喜欢

转载自blog.csdn.net/h_666666/article/details/87163652
今日推荐