LeetCode:Heap,Sort,Bit Manipulation,Backtracking

这篇博客详细介绍了LeetCode中涉及的堆、排序、位操作和回溯算法的题目,包括设计Twitter、找到数据流中的中位数、滑动窗口最大值、最频繁的元素等问题。博客通过实例解析了相关算法,并讨论了Java中数据结构的实现,如PriorityQueue、Deque和Stack。同时,文章还探讨了位操作如补码的原理和应用,以及回溯算法在解决N皇后问题等经典问题上的应用。
摘要由CSDN通过智能技术生成

15.Heap

先来复习一下stack,queue,heap的特点:
1.stack:FILO(先进后出)
2.queue:FIFO(先进先出)
3.heap:两个rules,1.the tree is complete,2.parent is always smaller than its two children.

参考differences

1.Design Twitter

题意

未实现显示前10状态的功能,如果要实现肯定是可以的,将Twitter写成一个类,类中包含成员变量时间,然后在getNewsFeed方法中取出每条twitter后再来一次排序,这样就能得到最新发表的前10条状态。但这样也有弊端,如果插入的时间间隔很短,很可能出现两条状态时间相同的情况,而维护一个队列或者其他不依靠绝对时间有序的方法可以避免这样bug情况。并且这样实现的方法都太naive了,想想更高级的实现方法吧。初级代码如下:

public class Twitter {
   

    //users关系
    Set<User> relations=new HashSet<>();
    /** Initialize your data structure here. */
    public Twitter() {

    }

    /** Compose a new tweet. */
    public void postTweet(int userId, int tweetId) {
        for (User each:
                relations) {
            if(each.id==userId){
                each.twitters.add(tweetId);
                return;
            }
        }
        User user = new User();
        user.id = userId;
        user.twitters.add(tweetId);
        relations.add(user);
    }

    /** Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent. */
    public List<Integer> getNewsFeed(int userId) {
        List<User> users = new ArrayList<>();
        for (User each:
                relations) {
            if(each.id==userId){
                users.add(each);
                users.addAll(each.followers);
            }
        }
        List<Integer> news = new ArrayList<>();
        for (User eachUser:
             users) {
            news.addAll(eachUser.twitters);
        }
        for (Integer each:
             news) {
            System.out.println("news:   "+each);
        }
        System.out.println("------");
        return news;
    }

    /** Follower follows a followee. If the operation is invalid, it should be a no-op. */
    public void follow(int followerId, int followeeId) {
        if(followerId==followeeId){
            return;
        }
        User follower = new User();
        follower.id = followerId;

        User followee = new User();
        followee.id = followeeId;

        follower.followers.add(followee);
        followee.followees.add(follower);

        relations.add(follower);
        relations.add(followee);
    }

    /** Follower unfollows a followee. If the operation is invalid, it should be a no-op. */
    public void unfollow(int followerId, int followeeId) {
        if(followerId==followeeId){
            return;
        }
        for (User each:
             relations) {
            if(each.id==followerId){
                for (User ss:
                     each.followers) {
                    if(ss.id==followeeId){
                        each.followers.remove(ss);
                        break;
                    }
                }
            }else if(each.id==followeeId){
                for (User ss:
                        each.followees) {
                    if(ss.id==followerId){
                        each.followees.remove(ss);
                        break;
                    }
                }
            }
        }
    }

}

class User{
    int id=Integer.MIN_VALUE;//用户标识
    List<Integer> twitters=new ArrayList<>();//发文
    Set<User> followees=new HashSet<>();//被哪些关注了
    Set<User> followers=new HashSet<>();//关注了哪些
}

比我高到不知道哪里的实现:

public class Twitter {
   
    //非常聪明,全局的time计数,这样每次不管哪个user发送twitter,自增1后都能保证是对的顺序,同时又避免使用庞大的Date等time类来解决
    //从这里看出换个角度来想问题的重要性
    private static int timeStamp=0;

    // easy to find if user exist
    //这样就不必像我实现的那样遍历Set,来看id是否匹配了!
    private Map<Integer, User> userMap;

    // Tweet link to next Tweet so that we can save a lot of time
    // when we execute getNewsFeed(userId)
    private class Tweet{
   
        public int id;
        public int time;
        public Tweet next;

        public Tweet(int id){
            this.id = id;
            //保证顺序
            time = timeStamp++;
            next=null;
        }
    }


    // OO design so User can follow, unfollow and post itself
    //用户调用方法
    public class User{
   
        public int id;
        public Set<Integer> followed;
        //只保存tweet head,节省空间
        public Tweet tweet_head;

        public User(int id){
            this.id=id;
            followed = new HashSet<>();
            follow(id); // first follow itself
            tweet_head = null;
        }

        public void follow(int id){
            followed.add(id);
        }

        public void unfollow(int id){
            followed.remove(id);
        }


        // everytime user post a new tweet, add it to the head of tweet list.
        public void post(int id){
            Tweet t = new Tweet(id);
            t.next=tweet_head;
            tweet_head=t;
        }
    }




    /** Initialize your data structure here. */
    public Twitter() {
        userMap = new HashMap<Integer, User>();
    }

    /** Compose a new tweet. */
    public void postTweet(int userId, int tweetId) {
        if(!userMap.containsKey(userId)){
            User u = new User(userId);
            userMap.put(userId, u);
        }
        userMap.get(userId).post(tweetId);

    }

    //重点部分
    // Best part of this.
    // first get all tweets lists from one user including itself and all people it followed.
    // Second add all heads into a max heap. Every time we poll a tweet with
    // largest time stamp from the heap, then we add its next tweet into the heap.
    // So after adding all heads we only need to add 9 tweets at most into this
    // heap before we get the 10 most recent tweet.
    public List<Integer> getNewsFeed(int userId) {
        List<Integer> res = new LinkedList<>();

        if(!userMap.containsKey(userId))   return res;

        Set<Integer> users = userMap.get(userId).followed;
        //注意该初始化操作,指定了capacity和comparator
        PriorityQueue<Tweet> q = new PriorityQueue<Tweet>(users.size(), (a,b)->(b.time-a.time));
        for(int user: users){
            //将tweet head存入priorityQueue中
            Tweet t = userMap.get(user).tweet_head;
            // very imporant! If we add null to the head we are screwed.
            if(t!=null){
                q.add(t);
            }
        }
        int n=0;
        while(!q.isEmpty() && n<10){
            Tweet t = q.poll();
            res.add(t.id);
            n++;
            //因为poll掉了head,所以将head.next加入进来
            if(t.next!=null)
                q.add(t.next);
        }
        return res;

    }

    /** Follower follows a followee. If the operation is invalid, it should be a no-op. */
    public void follow(int followerId, int followeeId) {
        if(!userMap.containsKey(followerId)){
            User u = new User(followerId);
            userMap.put(followerId, u);
        }
        if(!userMap.containsKey(followeeId)){
            User u = new User(followeeId);
            userMap.put(followeeId, u);
        }
        userMap.get(followerId).follow(followeeId);
    }

    /** Follower unfollows a followee. If the operation is invalid, it should be a no-op. */
    public void unfollow(int followerId, int followeeId) {
        if(!userMap.containsKey(followerId) || followerId==followeeId)
            return;
        userMap.get(followerId).unfollow(followeeId);
    }
}

2.Find Median from Data Stream

不断变化的数组寻找中位数。

题意

参考:
1.Short simple Java
2.max,min priorityQueue

//fail 1
//timeOut
// public class MedianFinder {
   
//     List<Integer> list = new ArrayList<>();
//     int pointer = -1;
//     boolean even=true;

//     // Adds a number into the data structure.
//     public void addNum(int num) {
   
//         list.add(num);
//         Collections(list);
//         even=!even;
//         if(even==false){
   
//             pointer++;
//         }
//     }

//     // Returns the median of current data stream
//     public double findMedian() {
   
//         if(even){
   
//             double sum = list.get(pointer)+list.get(pointer+1);
//             return sum/(double)2;
//         }else{
   
//             return list.get(pointer);
//         }
//     }
// }

//success 2
//维护两个priorityQueue
class MedianFinder {

    private Queue<Long> small = new PriorityQueue(),
                        large = new PriorityQueue();

    public void addNum(int num) {
        large.add((long) num);
        small.add(-large.poll());
        if (large.size() < small.size())
            large.add(-small.poll());
    }

    public double findMedian() {
        return large.size() > small.size()
               ? large.peek()
               : (large.peek() - small.peek()) / 2.0;
    }
}

这里补充priorityQueue如何遍历呢?很自然的想法是利用iterator,如下:

PriorityQueue<Integer> integers = new PriorityQueue<>(Collections.reverseOrder());//自然顺序的逆序
        integers.offer(7);
        integers.offer(9);
        integers.offer(3);
        integers.offer(5);
        //test iterator
        Iterator<Integer> i = integers.iterator();
        while(i.hasNext()){
            System.out.println(i.next());
        }
        System.out.println("------");
        System.out.println("size:   "+integers.size());
        //test poll
        for (int j = 0; j < 4; j++) {
   
            System.out.println(integers.poll());
        }

输出结果为:

9
7
3
5
------
size:   4
9
7
5
3

说明在priorityQueue中利用iterator保证顺序的遍历并不靠谱!该遍历方式没办法保证顺序!

javaDoc中讲到:

The Iterator provided in method iterator() is not guaranteed to traverse the elements of the PriorityQueue in any particular order. If you need ordered traversal, consider using Arrays.sort(pq.toArray()).

参考:
1.How to iterate over a PriorityQueue?

那么在Stack中使用iterator有没有这样的错误呢?实验:

Stack<Integer> integers = new Stack<>();
        integers.push(7);
        integers.push(9);
        integers.push(3);
        integers.push(5);
        //test iterator
        Iterator<Integer> i = integers.iterator();
        while(i.hasNext()){
            System.out.println(i.next());
        }
        System.out.println("------");
        System.out.println("size:   "+integers.size());
        //test pop
        for (int j = 0; j < 4; j++) {
   
            System.out.println(integers.pop());
        }

输出结果:

7
9
3
5
------
size:   4
5
3
9
7

参考javaDoc-bug(没看错,就是个java bug),申明到:

The iterator method on java.util.Stack iterates through a Stack from the bottom
up. One would think that it should iterate as if it were popping off the top of
the Stack.

Stack的iterator是从bottom到up进行遍历的,可以看出这种遍历是有顺序的,只是跟我们希望的从栈顶开始遍历的方式相反而已。

其实java中关于FILO(先进后出)数据结构更好的实现方式是Deque而不是Stack(因为兼容问题,并没有废除Stack,而是用Deque更好地实现,好怪,Stack居然不是Stack的最好实现,哈哈哈),参考javaDoc

The Stack class represents a last-in-first-out (LIFO) stack of objects. It extends class Vector with five operations that allow a vector to be treated as a stack. The usual push and pop operations are provided, as well as a method to peek at the top item on the stack, a method to test for whether the stack is empty, and a method to search the stack for an item and discover how far it is from the top.

When a stack is first created, it contains no items.

A more complete and consistent set of LIFO stack operations is provided by the Deque interface and its implementations, which should be used in preference to this class. For example:

   Deque<Integer> stack = new ArrayDeque<Integer>();

Deque的iterator实现就是我们想要的顺序了(从栈顶up到栈底bottom的遍历顺序)!

关于heap数据结构在java中实现标准就是PriorityQueue。参考:
1.javaDoc

An unbounded priority queue based on a priority heap. 

The elements of the priority queue are ordered according to their natural ordering, or by a Comparator provided at queue construction time, depending on which constructor 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值