zhulinyin

  • 首页

  • 归档

EventBus原理总结

发表于 2019-03-08

获取EventBus实例

1
2
3
4
5
6
7
8
9
10
11
12
public static EventBus getDefault() {
EventBus instance = defaultInstance;
if (instance == null) {
synchronized (EventBus.class) {
instance = EventBus.defaultInstance;
if (instance == null) {
instance = EventBus.defaultInstance = new EventBus();
}
}
}
return instance;
}

使用DCL双检测锁机制实现单例模式,也可以直接通过EventBus的builder()方法获取一个EventBusBuilder的实例,然后通过该建造者模式来个性化地定制自己的EventBus。

注册观察者

1
2
3
4
5
6
7
8
9
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}

该函数主要是获取观察者所有订阅方法(带@Subscribe注解的方法),并将观察者与这些方法进行订阅绑定。

  • List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass):该方法先从缓存当中尝试获取某个观察者中的所有订阅方法,如果没有可用缓存的话就从该类中查找订阅方法,并在返回结果之前将这些方法信息放置到缓存当中。查找的过程一般是从当前观察者出发到其所有的父类,利用反射获取所有带@Subscribe注解的方法并添加到一个列表中。
  • void subscribe(Object subscriber, SubscriberMethod subscriberMethod):该方法首先将观察者与订阅方法封装成一个Subscription对象,然后从缓存中获取一个CopyOnWriteArrayList集合,它是一种适用于读多写少场景的数据结构,是一种线程安全的数组型列表,主要用来存储一个事件类型所对应的全部的Subscription对象,最后根据订阅方法的优先级插入到该列表中。该方法还从缓存中通过订阅者获取了一个事件类型列表,并将订阅方法的事件类型添加进去。

被观察者发布事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);

if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}

这里的currentPostingThreadState是一个ThreadLocal类型的变量,其中存储了对应于当前线程的PostingThreadState对象,该对象中存储了当前线程对应的事件队列和线程的状态信息等。将事件添加进事件队列中并通过循环不断取出事件队列中的事件进行发布。

void postSingleEvent(Object event, PostingThreadState postingState):根据eventInheritance的值决定是否要同时遍历当前事件的所有父类的事件信息并进行分发。如果设置为true就将执行这一操作,并最终使用postSingleEventForEventType对每个事件类型进行处理。

boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass):通过事件类型从缓存中取得它对应的全部的Subscription,然后对得到的Subscription列表进行遍历,并依次调用postToSubscription方法执行事件的发布操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}

该方法根据订阅方法指定的threadMode信息来执行不同的发布策略。

Sharepreferences原理总结

发表于 2019-03-05 | 更新于 2019-03-06
  1. SharedPreferences getSharedPreferences(String name, int mode):根据应用名称和文件名获取SharedPreferencesImpl对象(该类实现了SharedPreferences接口),如果不存在则直接创建,创建SharedPreferencesImpl对象的过程是开启线程从硬盘读取文件。
  2. String getString(String key, @Nullable String defValue):从map中获取value。
  3. Editor edit():创建EditorImpl对象(该类实现了Editor接口),创建一个临时的map用于存储修改的键值对。
  4. Editor putString(String key, @Nullable String value):往临时的map中插入键值对。
  5. boolean commit():首先将sp的数据同步到最新状态,然后在当前线程写入文件,通知监听器数据已发生改变,最后返回写操作状态是否成功。
  6. void apply():和commit主要区别就是apply的写文件操作会在一个线程中执行,不会阻塞UI线程。

commit()和apply()的对比

如果我们使用SharedPreference的apply方法, 虽然该方法可以很快返回,并在其它线程里将键值对写入到文件系统,但是当Activity的onPause/onStop等方法被调用时,会等待写入到文件系统的任务完成,如果写入比较慢,主线程就会出现ANR问题。为了避免出现ANR问题,最好还是别使用apply操作,用commit方法最保险。如果担心在主线程调用commit方法会出现ANR,可以将所有的commit任务放到单线程池的线程里去执行。

算法设计与分析-Week20

发表于 2019-03-05

Queue Reconstruction by Height

题目描述

Suppose you have a random list of people standing in a queue. Each person is described by a pair of integers (h, k), where h is the height of the person and k is the number of people in front of this person who have a height greater than or equal to h. Write an algorithm to reconstruct the queue.

Note:
The number of people is less than 1,100.

Example:

Input:
[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]
Output:
[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]

解题思路

本题给出一个随机people数组,每个people由两个数组成,第一个数 h 表示people的身高,第二个数 k 表示在该people前面有 k 个人不比他矮,按这个要求重排people数组。
首先选出数组中最高的people,按他们的 k 值作为他们的下标插入到结果数组中,然后选出次高的people,同样按他们的 k 值作为他们的下标插入到结果数组中,以此类推。该做法基于两个原理:1.后插入的people由于身高比先插入的低,因此不管怎么插入都不会使得先插入的people违反要求;2.插入people时,结果数组中的people都是比自己高的,因此按 k 值为下标插入即可满足要求。

源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public:
vector<pair<int, int>> reconstructQueue(vector<pair<int, int>>& people) {
sort(people.begin(), people.end(), [](pair<int, int> a, pair<int, int> b) {
if(a.first > b.first) return true;
else if(a.first == b.first) return a.second < b.second;
return false;
});
vector<pair<int, int>> res;
for(pair<int, int> p : people) {
res.insert(res.begin() + p.second, p);
}
return res;
}
};

浅谈android中app启动流程

发表于 2019-03-01
  1. 从ActivityThread.java中的main()方法开始,初始化Looper,实例化ActivityThread,发送创建Application的消息,开启Looper的无限循环状态,接收消息。
  2. 发送创建Application的消息:通过Binder与AMS通信,通知AMS发送H.BIND_APPLICATION消息到主Handler中,主Handler接收到消息后,调用handleBindApplication()方法,初始化Instrumentation,通过Instrumentation调用Application的onCreate()方法从而创建了Application。
  3. 当Application初始化完成后,系统会根据Manifests中配置的启动Activity发送一个Intent去启动相应的Activity。通过AMS发送H.LAUNCH_ACTIVITY消息到主Handler中,主Hnadler接收到消息后,调用handleLaunchActivity()方法,该方法通过Instrumentation调用Activity的onCreate()创建activity。

图片来自 https://www.jianshu.com/p/9ecea420eb52

Java中的装箱和拆箱

发表于 2019-02-26

装箱和拆箱的定义

1
2
Integer i = 10;  //装箱
int n = i; //拆箱
  • 装箱: 自动将基本数据类型转换为包装器类型。
  • 拆箱: 自动将包装器类型转换为基本数据类型。

下表是基本数据类型对应的包装器类型:

基本数据类型 包装器类型
int Integer
byte Byte
short Short
long Long
float Float
double Double
char Character
boolean Boolean

装箱和拆箱的实现

装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。(xxx代表对应的基本数据类型)。

1
2
Integer i = 10;  //相当于Integer i = Integer.valueOf(10);
int n = i; //相当于int n = i.intValue();

常见问题

1
2
3
4
5
6
7
8
9
10
11
12
public class Main {
public static void main(String[] args) {

Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;

System.out.println(i1==i2); //true
System.out.println(i3==i4); //false
}
}

当通过valueOf方法创建Integer对象时,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。

弊端

自动装箱和拆箱虽然给我们编程提供了很多方便,但是使用不当容易产生大量的冗余对象,增加GC的压力。

算法设计与分析-Week19

发表于 2019-02-26

Remove K Digits

题目描述

Given a non-negative integer num represented as a string, remove k digits from the number so that the new number is the smallest possible.

Note:

  • The length of num is less than 10002 and will be ≥ k.
  • The given num does not contain any leading zero.

Example 1:

Input: num = “1432219”, k = 3
Output: “1219”
Explanation: Remove the three digits 4, 3, and 2 to form the new number 1219 which is the smallest.

Example 2:

Input: num = “10200”, k = 1
Output: “200”
Explanation: Remove the leading 1 and the number is 200. Note that the output must not contain leading zeroes.

Example 3:

Input: num = “10”, k = 2
Output: “0”
Explanation: Remove all the digits from the number and it is left with nothing which is 0.

解题思路

本题要求在给定的数字字符串中,删除k个数字,得到的数最小。
可以用一个字符串模拟栈操作,遍历给定的字符串,对于每一个数字字符,将其与栈中的字符作比较,若比栈顶字符小,就弹出栈顶字符并继续比较,弹出的操作即为删除字符的操作,因此必须限定最多弹出k个字符。

源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public:
string removeKdigits(string num, int k) {
string ans = "";
for(char c: num) {
while(ans.length() && ans.back() > c && k) {
ans.pop_back();
k--;
}
if(ans.length() || c != '0') ans.push_back(c);
}
while(ans.length() && k--) ans.pop_back();
return ans.empty() ? "0" : ans;
}
};

android中的设计模式

发表于 2019-02-21

单例模式

单例模式保证了在程序运行的过程中,某个实现了单例模式的类只有一个实例。

实现方式

  1. 饿汉式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Singleton {
    private static Singleton instance = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
    retun instance;
    }
    }
    • 优点:线程安全
    • 缺点:类加载的时候就实例化静态对象,若程序运行过程不需用到该对象则浪费了资源
  2. 懒汉式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Singleton {
    private static Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance () {
    if (instance == null) {
    instatnce = new Singleton();
    }
    return instance;
    }
    }
    • 优点:延迟加载
    • 缺点:线程不安全
  3. 懒汉式加锁

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Singleton {
    private static Singleton instance = null;

    private Singleton() {
    }

    public static synchronized Singleton getInstance () {
    if (instance == null) {
    instatnce = new Singleton();
    }
    return instance;
    }
    }
    • 优点:延迟加载且线程安全
    • 缺点:造成不必要的同步开销
  4. 双重锁

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class Singleton {
    private static Singleton instance = null;

    private Singleton () {
    }

    public static Singleton getInstance () {
    // If already initialized, no need to get lock everytime.
    if (instance == null) {
    synchronized (Singleton.class) {
    if (instance == null) {
    instance = new Singleton();
    }
    }
    }
    return instance;
    }
    }
    • 优点:延迟加载,解决了多余的同步,线程安全(两次判空,第一层是为了避免不必要的同步。 第二层是为了在instance为null的情况下创建实例)
  5. 静态内部类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Singleton() {

    private Singleton() {
    }

    public static Singleton getInstance () {
    return SingletonHolder.instance;
    }

    /**
    * 静态内部类,只有在装载该内部类时才会去创建单例对象
    */
    private static class SingletonHolder {
    private static final Singleton instance = new Singleton();
    }

    }
    • 优点:利用Java虚拟机加载类的特性实现延迟加载和线程安全

建造者模式

如果一个类的构造需要很多参数,但是这些参数并不都是必须的,这种情况适合使用建造者模式,如AlertDialog.Builder。

观察者模式

如果想要在某个对象发生变化时立刻收到通知,则使用观察者模式,如各种点击监听事件,rxjava等。

原型模式

如果持有一个对象,想要快速获取一个相同属性的对象,则使用原型模式,常见的是实现clone()方法。

责任链模式

如果有一个任务从一个节点出发,沿着一条链不断传递,直到找到处理该任务的节点,则为责任链模式,如android的事件分发机制。

工厂模式

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。如BitmapFactory通过各种decodeXXX()就可以从不同渠道获得Bitmap对象。

策略模式

策略模式的精髓就在于,你传入一个类,后面的处理就能按照这个类的实现去做。以动画为例,设置不同的插值器对象,就可以得到不同的变化曲线。

适配器模式

适配器模式是作为两个不兼容的接口之间的桥梁。android中有各种adapter,主要就是适配数据在布局中的显示。

装饰器模式

装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。具体可参考装饰器模式。

android动画总结

发表于 2019-02-19

Android的动画可分为逐帧动画、补间动画、属性动画(Android3.0后的新特性)

1.逐帧动画

原理

  • 将动画拆分为帧的形式,每一帧=一张图片
  • 逐帧动画就是按序播放一组预先定义好的图片

具体使用

  1. 将动画资源(即每一张图片)放入drawable文件夹
  2. 设置 & 启动动画

    • 在 res/anim的文件夹里创建动画效果.xml文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      <?xml version="1.0" encoding="utf-8"?>
      <animation-list
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:oneshot="true" // 设置是否只播放一次,默认为false
      >

      // item = 动画图片资源;duration = 设置一帧持续时间(ms)
      <item android:drawable="@drawable/a0" android:duration="100"/>
      <item android:drawable="@drawable/a1" android:duration="100"/>
      <item android:drawable="@drawable/a2" android:duration="100"/>
      <item android:drawable="@drawable/a3" android:duration="100"/>
      <item android:drawable="@drawable/a4" android:duration="100"/>
      <item android:drawable="@drawable/a5" android:duration="100"/>
      </animation-list>
    • 载入 & 启动动画

      1
      2
      3
      4
      5
      6
      imageView.setImageResource(R.drawable.anim);
      // 1. 设置动画
      AnimationDrawable animationDrawable = (AnimationDrawable) imageView.getDrawable();
      // 2. 获取动画对象
      animationDrawable.start();
      // 3. 启动动画

特点

  • 优点:使用简单、方便
  • 缺点:容易引起 OOM,因为会使用大量 & 尺寸较大的图片资源

2.补间动画

原理

  • 通过确定开始的视图样式 & 结束的视图样式、中间动画变化过程由系统补全来确定一个动画。
  • 根据不同的动画效果,补间动画分为平移动画、缩放动画、旋转动画、透明度动画。

    具体使用(以平移动画为例)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Button mButton = (Button) findViewById(R.id.Button);
    // 步骤1:创建 需要设置动画的 视图View

    Animation translateAnimation = new TranslateAnimation(0,500,0,500);
    // 步骤2:创建平移动画的对象:平移动画对应的Animation子类为TranslateAnimation
    // 参数分别是:
    // 1. fromXDelta :视图在水平方向x 移动的起始值
    // 2. toXDelta :视图在水平方向x 移动的结束值
    // 3. fromYDelta :视图在竖直方向y 移动的起始值
    // 4. toYDelta:视图在竖直方向y 移动的结束值

    translateAnimation.setDuration(3000);
    // 固定属性的设置都是在其属性前加“set”,如setDuration()
    mButton.startAnimation(translateAnimation);
    // 步骤3:播放动画

3.属性动画

原理

  • 补间动画 只能够作用在视图View上,即只可以对一个Button、TextView、甚至是LinearLayout、或者其它继承自View的组件进行动画操作,但无法对非View的对象进行动画操作。
  • 补间动画只是改变了View的视觉效果,而不会真正去改变View的属性。
  • 在一定时间间隔内,通过不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果。

    具体使用

    ValueAnimator类

  1. 原理:通过不断控制属性值的变化,再不断手动赋给对象的属性,从而实现动画效果。
  2. 以ValueAnimator.ofInt为例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    // 步骤1:设置动画属性的初始值 & 结束值
    ValueAnimator anim = ValueAnimator.ofInt(0, 3);
    // ofInt()作用有两个
    // 1. 创建动画实例
    // 2. 将传入的多个Int参数进行平滑过渡:此处传入0和1,表示将值从0平滑过渡到1
    // 如果传入了3个Int参数 a,b,c ,则是先从a平滑过渡到b,再从b平滑过渡到C,以此类推
    // ValueAnimator.ofInt()内置了整型估值器,直接采用默认的.不需要设置,即默认设置了如何从初始值 过渡到 结束值

    // 步骤2:设置动画的播放各种属性
    anim.setDuration(500);
    // 设置动画运行的时长

    anim.setStartDelay(500);
    // 设置动画延迟播放时间

    anim.setRepeatCount(0);
    // 设置动画重复播放次数 = 重放次数+1
    // 动画播放次数 = infinite时,动画无限重复

    anim.setRepeatMode(ValueAnimator.RESTART);
    // 设置重复播放动画模式
    // ValueAnimator.RESTART(默认):正序重放
    // ValueAnimator.REVERSE:倒序回放

    // 步骤3:将改变的值手动赋值给对象的属性值:通过动画的更新监听器
    // 设置 值的更新监听器
    // 即:值每次改变、变化一次,该方法就会被调用一次
    anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {

    int currentValue = (Integer) animation.getAnimatedValue();
    // 获得改变后的值

    System.out.println(currentValue);
    // 输出改变后的值

    // 步骤4:将改变后的值赋给对象的属性值,下面会详细说明
    View.setproperty(currentValue);

    // 步骤5:刷新视图,即重新绘制,从而实现动画效果
    View.requestLayout();

    }
    });

    anim.start();
    // 启动动画

ObjectAnimator类

  1. 原理:通过不断控制属性值的变化,再不断自动赋给对象的属性,从而实现动画效果。
  2. 以ObjectAnimator.ofFloat为例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    ObjectAnimator animator = ObjectAnimator.ofFloat(Object object, String property, float ....values);  

    // ofFloat()作用有两个
    // 1. 创建动画实例
    // 2. 参数设置:参数说明如下
    // Object object:需要操作的对象
    // String property:需要操作的对象的属性
    // float ....values:动画初始值 & 结束值(不固定长度)
    // 若是两个参数a,b,则动画效果则是从属性的a值到b值
    // 若是三个参数a,b,c,则则动画效果则是从属性的a值到b值再到c值
    // 以此类推
    // 至于如何从初始值 过渡到 结束值,同样是由估值器决定,此处ObjectAnimator.ofFloat()是有系统内置的浮点型估值器FloatEvaluator,同ValueAnimator讲解

    anim.setDuration(500);
    // 设置动画运行的时长

    anim.setStartDelay(500);
    // 设置动画延迟播放时间

    anim.setRepeatCount(0);
    // 设置动画重复播放次数 = 重放次数+1
    // 动画播放次数 = infinite时,动画无限重复

    anim.setRepeatMode(ValueAnimator.RESTART);
    // 设置重复播放动画模式
    // ValueAnimator.RESTART(默认):正序重放
    // ValueAnimator.REVERSE:倒序回放

    animator.start();
    // 启动动画

算法设计与分析-Week18

发表于 2019-02-19

Advantage Shuffle

题目描述

Given two arrays A and B of equal size, the advantage of A with respect to B is the number of indices i for which A[i] > B[i].
Return any permutation of A that maximizes its advantage with respect to B.

Example 1:

Input: A = [2,7,11,15], B = [1,10,4,11]
Output: [2,11,7,15]

Example 2:

Input: A = [12,24,8,32], B = [13,25,32,11]
Output: [24,32,8,12]

解题思路

本题实际上是田忌赛马问题,要求找出A数组的一种排列方式使得其对于B数组优势最大。
解题思路借助于田忌赛马,用最差的马跟对方最好的马比赛,用略好的马跟对方略差的马比赛,利用这种贪心策略,我遍历B数组的每个元素,对于每个元素在A数组中找到一个略大于它的元素;如果找不到,则用A数组的最小元素。

源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
vector<int> advantageCount(vector<int>& A, vector<int>& B) {
multiset<int> s(begin(A), end(A));
vector<int> res;
for (int b : B) {
auto it = s.upper_bound(b);
if (it == s.end()) it = s.begin();
res.push_back(*it);
s.erase(it);
}
return res;
}
};

算法设计与分析-Week17

发表于 2019-02-12

Russian Doll Envelopes

题目描述

A robot is located at the top-left corner of a m x n grid (marked ‘Start’ in the diagram below).
The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked ‘Finish’ in the diagram below).
Now consider if some obstacles are added to the grids. How many unique paths would there be?

An obstacle and empty space is marked as 1 and 0 respectively in the grid.
Note: m and n will be at most 100.
Example 1:

Input:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
Output: 2
Explanation:
There is one obstacle in the middle of the 3x3 grid above.
There are two ways to reach the bottom-right corner:
1.Right -> Right -> Down -> Down
2.Down -> Down -> Right -> Right

解题思路

本题要求从矩阵的左上角到右下角一共有多少种不同的路径,限制只能往右和往下走且中间可能有障碍。
用一个矩阵path,path[i][j]表示到达i行和j列有多少不同的路径,初始化第一行和第一列为1,且如果中间有障碍,后面均为0,例如第一行中间有障碍,障碍后面的路径数均为0,因为向右走无法到达。若不为第一行和第一列,如果第i行第j列不为障碍,则path[i][j] = path[i-1][j] + path[i][j-1];如果第i行第j列为障碍,则path[i][j] = 0。为了节省空间,可用一个一维的数组代替path矩阵。

源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int n = obstacleGrid.size();
int m = obstacleGrid[0].size();
if(obstacleGrid[0][0] == 1 || obstacleGrid[n-1][m-1] == 1) return 0;
vector<long long> path(m, 1);
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(obstacleGrid[i][j] == 1) path[j] = 0;
else if(j == 0) {
path[j] = path[j] == 0 ? 0 : 1;
}
else if(i == 0) {
path[j] = path[j-1] == 0 ? 0 : 1;
}
else path[j] = path[j] + path[j-1];
}
}
return path[m-1];
}
};
123…5

Li Jiangtao

48 日志
14 标签
© 2019 Li Jiangtao
由 Hexo 强力驱动 v3.7.0
|
主题 – NexT.Mist v7.1.1
0%