2015年11月26日星期四

CUDA编程小窥

并行计算课程可选部分有CUDA编程,这两周在系里的集群上感受了一下,非常不错。

一个字总结,好用易上手。

远程SSH上学校的主机,ssh peikun.zhou@129.175.237.164,然后再用申请的临时账号ssh进GPU集群,ssh -X zhou@tp-jetson. 

拷代码过去,scp helloworld.cu zhou@tp-jetson:/home/zhou/

1.先来一个简单的例子,helloworld.cu


#include <stdio.h>



__device__ const char *STR = "HELLO WORLD!";

const char STR_LENGTH = 12;



__global__ void hello()
{
        printf("%c\n", STR[threadIdx.x % STR_LENGTH]);
}

int main(void)
{
        int num_threads = STR_LENGTH;
        int num_blocks = 1;
        hello<<<num_blocks,num_threads>>>();
        cudaDeviceSynchronize();

        return 0;
}

1).__global__表明是在GPU Device上运行

2).hello<<<num_blocks,num_threads>>>();

Triple angle brackets mark a call from host code to device code, Also called a “kernel launch”

这部分是并行计算的代码,即GPU并行计算的代码。每一个block里有个若干个thread,这句的意思是有num_blocks个block,每一个Bloack里有num_threads个thread.

我们须知,在GPU里,每一个Block都是并行计算的。

3).nVIDIA的编译器,即nvcc可以编译常规的C语言代码(Host Code非Device code)

nvcc helloworld.cu编译之,./hello运行结果,如下,

zhou@tegra-ubuntu:~$ ./hello                                                                           
H                                                                                                      
E                                                                                                      
L
L
O
W
O
R
L
D
!

这是12个并行输出(计算)的结果。


2.向量加法运算

#include <stdio.h>

__global__ void vector_add(int *a, int *b, int *c)
{
    /* insert code to calculate the index properly using blockIdx.x, blockDim.x, threadIdx.x */
        int index = threadIdx.x + blockIdx.x * blockDim.x;
        c[index] = a[index] + b[index];
}

/* experiment with N */
/* how large can it be? */
#define N (2048*2048)
#define THREADS_PER_BLOCK 512

int main()
{
  int *a, *b, *c;
        int *d_a, *d_b, *d_c;

        int size = N * sizeof( int );

        /* allocate space for device copies of a, b, c */
        cudaMalloc( (void **) &d_a, size );
        cudaMalloc( (void **) &d_b, size );
        cudaMalloc( (void **) &d_c, size );

        /* allocate space for host copies of a, b, c and setup input values */

        a = (int *)malloc( size );
        b = (int *)malloc( size );
        c = (int *)malloc( size );

        for( int i = 0; i < N; i++ )
        {
                a[i] = b[i] = i;
                c[i] = 0;
        }

        /* copy inputs to device */
        /* fix the parameters needed to copy data to the device */
        cudaMemcpy( d_a, a, size, cudaMemcpyHostToDevice );
        cudaMemcpy( d_b, b, size, cudaMemcpyHostToDevice );

        /* launch the kernel on the GPU */
        /* insert the launch parameters to launch the kernel properly using blocks and threads */
        vector_add<<< N/THREADS_PER_BLOCK, THREADS_PER_BLOCK >>>( d_a, d_b, d_c );

        /* copy result back to host */
        /* fix the parameters needed to copy data back to the host */
        cudaMemcpy(c, d_c, size, cudaMemcpyDeviceToHost );


        printf( "c[0] = %d\n",0,c[0] );
        printf( "c[%d] = %d\n",N-1, c[N-1] );

        /* clean up */

        free(a);
        free(b);
        free(c);
        cudaFree( d_a );
        cudaFree( d_b );
        cudaFree( d_c );

        return 0;
} /* end main */
                     
注意看英文注释。

我们看CUDA内置的很多函数,都是C/C++的影子,如这里的cudaMemcpy(),cudaMalloc(),用法大同小异,非常容易上手。                                                

左总结右总结,想梳理一下CUDA并行计算的过程,还是说不好,即然这样就偷懒把官方的入门文档放一下。






KDE任务栏找回消失的当前任务缩略

今天开始摸索KDE,在ubuntu14.05上装的,感觉非常不错,相见恨晚。

说说优点,

1.字体无可挑剔,比Unity默认的楷书、渲染强太多,终于不用耗费精力折腾字体

2.主题美观大方,晶莹剔透,个人认为可以与mac os有的一拼,期待未来的扁平化

3.屏幕八点可定义,之前挺喜欢Gnome上这个功能

4.切换效果靓丽,无Unity大概率的卡顿

5.任务栏可定制,自由度很高,很容易将其做成Dock的样子

6.....想到了再补充


在摸索的时候,马上就遇到了一个小问题,

1.任务栏/面板(Panel)上的当前任务缩略给弄丢了,就是任务栏找不到当前正在运行的程序,只能靠Alt+Table来切换

摸索许久,终于找了回来。任务栏上Add Weights -> 添加Task Manager即可。

原来我们任务栏上的任务缩略也算是其上的一个Weight,恕我浅陋无知了。






2015年11月19日星期四

Install Sublime Text 2 in unbuntu

Method 1 - Easiest
As Jeff T points out in the comments below, it is actually quite simple to install Sublime Text 2. Here are the commands:

sudo add-apt-repository ppa:webupd8team/sublime-text-2
sudo apt-get update
sudo apt-get install sublime-text


Method 2

Download and Extract the tar file
First, head over to Sublime's website, and download the tar file for linux. After you have it downloaded, extract it with the following command (yours may vary, depending on the version you downloaded).

# unzip the file
tar xf Sublime\ Text\ 2.0.1\ x64.tar.bz2
You should see a Sublime Text 2 folder after you finish extracting the contents.

Move the extracted contents
After extracting the file, we are going to move it to the /opt directory.

sudo mv Sublime\ Text\ 2 /opt/

Launching from Terminal
Next, we are going to make it so we can simply type "sublime" into the terminal in order to launch it. To do so, run the following command:

sudo ln -s /opt/Sublime\ Text\ 2/sublime_text /usr/bin/sublime

Creating a Launcher in Unity
You may also want to create a launcher for Sublime (these commands are specific to Unity).

# create launcher in Unity
sudo sublime /usr/share/applications/sublime.desktop
Once sublime opens, paste the following content into sublime.desktop

# paste the following content into sublime.desktop
[Desktop Entry]
Version=1.0
Name=Sublime Text 2
GenericName=Text Editor

Exec=sublime
Terminal=false
Icon=/opt/Sublime Text 2/Icon/48x48/sublime_text.png
Type=Application
Categories=TextEditor;IDE;Development
X-Ayatana-Desktop-Shortcuts=NewWindow

[NewWindow Shortcut Group]
Name=New Window
Exec=sublime -n
TargetEnvironment=Unity

Default Text Editor
If you would like to set Sublime Text 2 as your default text editor, run the following command, and replace every instance of gedit.desktop with sublime.desktop.

# You will need to replace every instance of gedit.desktop with sublime.desktop
sudo sublime /usr/share/applications/defaults.list

That's it! You should not only have a luncher available to run Sublime, but you can also launch it via the terminal by just typing "sublime". If you have any questions, feel free to leave them in the comments below!

2015年11月9日星期一

#LeetCode# Binary Tree Level Order Traversal II

Given a binary tree, return the bottom-up level order traversal of its nodes' values. (ie, from left to right, level by level from leaf to root).
For example:
Given binary tree {3,9,20,#,#,15,7},
    3
   / \
  9  20
    /  \
   15   7
return its bottom-up level order traversal as:
[
  [15,7],
  [9,20],
  [3]
]

此题是上一道题的小变形,我们直接将上一道题的结果倒序整理一次即可。

可是实际操作中,遇到了新的问题,

定义一个新的reverseResult动态数组,ArrayList<ArrayList<Integer>> reverseResult = new ArrayList<ArrayList<Integer>> ();
运行,出现如下错误,

Line 44: error: no suitable method found for add(List<Integer>)

分析一下。

之前的result是 ArrayList<List<Integer>> result = new ArrayList<List<Integer>> ();

我们想将result中的结果用.add()方法加入到reverseResult中去,却提示没有相应的add(List<Integer>)方法。

仔细比较一下result和reverseResult,可以发现,我们的定义中,result是存储List的动态数组(虽然我们实际加入的nodeValues是ArrayList),而reverseResult是存储数组的动态数组。

前者的List<Integer>无法直接add进入后者的ArrayList<Integer>中,而ArrayList是可以加入到List中。

所以,我们需要修改reverseResult的定义,为 ArrayList<List<Integer>> reverseResult = new ArrayList<List<Integer>> (),in this way,我们的编译通过了!这里依旧是List和ArrayList的问题。


好了,戴上套套,上代码吧,

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        ArrayList<List<Integer>> result = new ArrayList<List<Integer>> ();
        List<Integer> nodeValues = new ArrayList<Integer> ();
        ArrayList<List<Integer>> reverseResult = new ArrayList<List<Integer>> ();
        
        if(root == null) {
            return result;
        }
        
        LinkedList<TreeNode> current = new LinkedList<TreeNode> ();
        LinkedList<TreeNode> next = new LinkedList<TreeNode> ();
        current.add(root);
        
        while(!current.isEmpty()) {
            TreeNode node = current.remove();
            nodeValues.add(node.val);
            
            if(node.left != null) {
                next.add(node.left);
            }
            if(node.right != null) {
                next.add(node.right);
            }
            
            if(current.isEmpty()) {
                result.add(nodeValues);
                current = next;
                next = new LinkedList();
                nodeValues = new ArrayList();
            }
        }
        
        for (int i = result.size() - 1; i >= 0; i--) {
            reverseResult.add(result.get(i));
        }
        
        return reverseResult;
        
    }

}

~~这周要把BFS刷掉!

Java的List总结(LinkedList与ArratList等)

谷歌LinkedList和ArrayList的时候,发现了一篇好文,转载之,笔记之。这里是原文链接


第1部分 List概括

先回顾一下List的框架图
(01) List 是一个接口,它继承于Collection的接口。它代表着有序的队列。
(02) AbstractList 是一个抽象类,它继承于AbstractCollection。AbstractList实现List接口中除size()、get(int location)之外的函数。
(03) AbstractSequentialList 是一个抽象类,它继承于AbstractList。AbstractSequentialList 实现了“链表中,根据index索引值操作链表的全部函数”。
(04) ArrayList, LinkedList, Vector, Stack是List的4个实现类。
  ArrayList 是一个数组队列,相当于动态数组。它由数组实现,随机访问效率高,随机插入、随机删除效率低。
  LinkedList 是一个双向链表。它也可以被当作堆栈、队列或双端队列进行操作。LinkedList随机访问效率低,但随机插入、随机删除效率低。
  Vector 是矢量队列,和ArrayList一样,它也是一个动态数组,由数组实现。但是ArrayList是非线程安全的,而Vector是线程安全的。
  Stack 是栈,它继承于Vector。它的特性是:先进后出(FILO, First In Last Out)。

第2部分 List使用场景

学东西的最终目的是为了能够理解、使用它。下面先概括的说明一下各个List的使用场景后面再分析原因
如果涉及到“栈”、“队列”、“链表”等操作,应该考虑用List,具体的选择哪个List,根据下面的标准来取舍。
(01) 对于需要快速插入,删除元素,应该使用LinkedList。
(02) 对于需要快速随机访问元素,应该使用ArrayList。
(03) 对于“单线程环境” 或者 “多线程环境,但List仅仅只会被单个线程操作”,此时应该使用非同步的类(如ArrayList)。
       对于“多线程环境,且List可能同时被多个线程操作”,此时,应该使用同步的类(如Vector)。

通过下面的测试程序,我们来验证上面的(01)和(02)结论。参考代码如下:
 View Code
运行结果如下
复制代码
Stack : insert 100000 elements into the 1st position use time:1640 ms
Vector : insert 100000 elements into the 1st position use time:1607 ms
LinkedList : insert 100000 elements into the 1st position use time:29 ms
ArrayList : insert 100000 elements into the 1st position use time:1617 ms

Stack : read 100000 elements by position use time:9 ms
Vector : read 100000 elements by position use time:6 ms
LinkedList : read 100000 elements by position use time:10809 ms
ArrayList : read 100000 elements by position use time:5 ms

Stack : delete 100000 elements from the 1st position use time:1916 ms
Vector : delete 100000 elements from the 1st position use time:1910 ms
LinkedList : delete 100000 elements from the 1st position use time:15 ms
ArrayList : delete 100000 elements from the 1st position use time:1909 ms
复制代码
从中,我们可以发现
插入10万个元素,LinkedList所花时间最短:29ms
删除10万个元素,LinkedList所花时间最短:15ms
遍历10万个元素,LinkedList所花时间最长:10809 ms;而ArrayList、Stack和Vector则相差不多,都只用了几秒。
考虑到Vector是支持同步的,而Stack又是继承于Vector的;因此,得出结论:
(01) 对于需要快速插入,删除元素,应该使用LinkedList。
(02) 对于需要快速随机访问元素,应该使用ArrayList。
(03) 对于“单线程环境” 或者 “多线程环境,但List仅仅只会被单个线程操作”,此时应该使用非同步的类。

第3部分 LinkedList和ArrayList性能差异分析

下面我们看看为什么LinkedList中插入元素很快,而ArrayList中插入元素很慢
LinkedList.java中向指定位置插入元素的代码如下
复制代码
// 在index前添加节点,且节点的值为element
public void add(int index, E element) {
    addBefore(element, (index==size ? header : entry(index)));
}

// 获取双向链表中指定位置的节点
private Entry<E> entry(int index) {
    if (index < 0 || index >= size)
        throw new IndexOutOfBoundsException("Index: "+index+
                                            ", Size: "+size);
    Entry<E> e = header;
    // 获取index处的节点。
    // 若index < 双向链表长度的1/2,则从前向后查找;
    // 否则,从后向前查找。
    if (index < (size >> 1)) {
        for (int i = 0; i <= index; i++)
            e = e.next;
    } else {
        for (int i = size; i > index; i--)
            e = e.previous;
    }
    return e;
}

// 将节点(节点数据是e)添加到entry节点之前。
private Entry<E> addBefore(E e, Entry<E> entry) {
    // 新建节点newEntry,将newEntry插入到节点e之前;并且设置newEntry的数据是e
    Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
    // 插入newEntry到链表中
    newEntry.previous.next = newEntry;
    newEntry.next.previous = newEntry;
    size++;
    modCount++;
    return newEntry;
}
复制代码
从中,我们可以看出:通过add(int index, E element)向LinkedList插入元素时。先是在双向链表中找到要插入节点的位置index;找到之后,再插入一个新节点
双向链表查找index位置的节点时,有一个加速动作若index < 双向链表长度的1/2,则从前向后查找; 否则,从后向前查找
接着,我们看看ArrayList.java中向指定位置插入元素的代码。如下:
复制代码
// 将e添加到ArrayList的指定位置
public void add(int index, E element) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(
        "Index: "+index+", Size: "+size);

    ensureCapacity(size+1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
         size - index);
    elementData[index] = element;
    size++;
}
复制代码
ensureCapacity(size+1) 的作用是“确认ArrayList的容量,若容量不够,则增加容量。
真正耗时的操作是 System.arraycopy(elementData, index, elementData, index + 1, size - index);
Sun JDK包的java/lang/System.java中的arraycopy()声明如下:
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
arraycopy()是个JNI函数,它是在JVM中实现的。sunJDK中看不到源码,不过可以在OpenJDK包中看到的源码。网上有对arraycopy()的分析说明,请参考:System.arraycopy源码分析 
实际上,我们只需要了解: System.arraycopy(elementData, index, elementData, index + 1, size - index); 会移动index之后所有元素即可这就意味着,ArrayList的add(int index, E element)函数,会引起index之后所有元素的改变!

通过上面的分析,我们就能理解为什么LinkedList中插入元素很快,而ArrayList中插入元素很慢。
“删除元素”与“插入元素”的原理类似,这里就不再过多说明。

接下来,我们看看 “为什么LinkedList中随机访问很慢,而ArrayList中随机访问很快”
先看看LinkedList随机访问的代码
复制代码
// 返回LinkedList指定位置的元素
public E get(int index) {
    return entry(index).element;
}

// 获取双向链表中指定位置的节点
private Entry<E> entry(int index) {
    if (index < 0 || index >= size)
        throw new IndexOutOfBoundsException("Index: "+index+
                                            ", Size: "+size);
    Entry<E> e = header;
    // 获取index处的节点。
    // 若index < 双向链表长度的1/2,则从前先后查找;
    // 否则,从后向前查找。
    if (index < (size >> 1)) {
        for (int i = 0; i <= index; i++)
            e = e.next;
    } else {
        for (int i = size; i > index; i--)
            e = e.previous;
    }
    return e;
}
复制代码
从中,我们可以看出:通过get(int index)获取LinkedList第index个元素时先是在双向链表中找到要index位置的元素;找到之后再返回。
双向链表查找index位置的节点时,有一个加速动作若index < 双向链表长度的1/2,则从前向后查找; 否则,从后向前查找。
下面看看ArrayList随机访问的代码 
复制代码
// 获取index位置的元素值
public E get(int index) {
    RangeCheck(index);

    return (E) elementData[index];
}

private void RangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(
        "Index: "+index+", Size: "+size);
}
复制代码
从中,我们可以看出:通过get(int index)获取ArrayList第index个元素时。直接返回数组中index位置的元素,而不需要像LinkedList一样进行查找。

第4部分 Vector和ArrayList比较

相同之处
1 它们都是List
它们都继承于AbstractList,并且实现List接口。
ArrayList和Vector的类定义如下:
复制代码
// ArrayList的定义
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

// Vector的定义
public class Vector<E> extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {}
复制代码
  
2 它们都实现了RandomAccess和Cloneable接口
   实现RandomAccess接口,意味着它们都支持快速随机访问;
   实现Cloneable接口,意味着它们能克隆自己。

3 它们都是通过数组实现的,本质上都是动态数组
ArrayList.java中定义数组elementData用于保存元素
// 保存ArrayList中数据的数组
private transient Object[] elementData;
Vector.java中也定义了数组elementData用于保存元素
// 保存Vector中数据的数组
protected Object[] elementData;

4 它们的默认数组容量是10
   若创建ArrayList或Vector时,没指定容量大小;则使用默认容量大小10。
ArrayList的默认构造函数如下:
// ArrayList构造函数。默认容量是10。
public ArrayList() {
    this(10);
}
Vector的默认构造函数如下:
// Vector构造函数。默认容量是10。
public Vector() {
    this(10);
} 

5 它们都支持Iterator和listIterator遍历
   它们都继承于AbstractList,而AbstractList中分别实现了 “iterator()接口返回Iterator迭代器” 和 “listIterator()返回ListIterator迭代器”。

不同之处
1 线程安全性不一样
   ArrayList是非线程安全;
   而Vector是线程安全的,它的函数都是synchronized的,即都是支持同步的。
   ArrayList适用于单线程,Vector适用于多线程。

2 对序列化支持不同
   ArrayList支持序列化,而Vector不支持;即ArrayList有实现java.io.Serializable接口,而Vector没有实现该接口。

3 构造函数个数不同
   ArrayList有3个构造函数,而Vector有4个构造函数。Vector除了包括和ArrayList类似的3个构造函数之外,另外的一个构造函数可以指定容量增加系数。
ArrayList的构造函数如下
复制代码
// 默认构造函数
ArrayList()

// capacity是ArrayList的默认容量大小。当由于增加数据导致容量不足时,容量会添加上一次容量大小的一半。
ArrayList(int capacity)

// 创建一个包含collection的ArrayList
ArrayList(Collection<? extends E> collection)
复制代码
Vector的构造函数如下
复制代码
// 默认构造函数
Vector()

// capacity是Vector的默认容量大小。当由于增加数据导致容量增加时,每次容量会增加一倍。
Vector(int capacity)

// 创建一个包含collection的Vector
Vector(Collection<? extends E> collection)

// capacity是Vector的默认容量大小,capacityIncrement是每次Vector容量增加时的增量值。
Vector(int capacity, int capacityIncrement)
复制代码

4 容量增加方式不同
   逐个添加元素时,若ArrayList容量不足时,“新的容量”=“(原始容量x3)/2 + 1”。
   而Vector的容量增长与“增长系数有关”,若指定了“增长系数”,且“增长系数有效(即,大于0)”;那么,每次容量不足时,“新的容量”=“原始容量+增长系数”。若增长系数无效(即,小于/等于0),则“新的容量”=“原始容量 x 2”。
ArrayList中容量增长的主要函数如下:
复制代码
public void ensureCapacity(int minCapacity) {
    // 将“修改统计数”+1
    modCount++;
    int oldCapacity = elementData.length;
    // 若当前容量不足以容纳当前的元素个数,设置 新的容量=“(原始容量x3)/2 + 1”
    if (minCapacity > oldCapacity) {
        Object oldData[] = elementData;
        int newCapacity = (oldCapacity * 3)/2 + 1;
        if (newCapacity < minCapacity)
            newCapacity = minCapacity;
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
}
复制代码
Vector中容量增长的主要函数如下:
复制代码
private void ensureCapacityHelper(int minCapacity) {
    int oldCapacity = elementData.length;
    // 当Vector的容量不足以容纳当前的全部元素,增加容量大小。
    // 若 容量增量系数>0(即capacityIncrement>0),则将容量增大当capacityIncrement
    // 否则,将容量增大一倍。
    if (minCapacity > oldCapacity) {
        Object[] oldData = elementData;
        int newCapacity = (capacityIncrement > 0) ?
            (oldCapacity + capacityIncrement) : (oldCapacity * 2);
        if (newCapacity < minCapacity) {
            newCapacity = minCapacity;
        }
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
}
复制代码

5 对Enumeration的支持不同。Vector支持通过Enumeration去遍历,而List不支持
Vector中实现Enumeration的代码如下:
复制代码
public Enumeration<E> elements() {
    // 通过匿名类实现Enumeration
    return new Enumeration<E>() {
        int count = 0;

        // 是否存在下一个元素
        public boolean hasMoreElements() {
            return count < elementCount;
        }

        // 获取下一个元素
        public E nextElement() {
            synchronized (Vector.this) {
                if (count < elementCount) {
                    return (E)elementData[count++];
                }
            }
            throw new NoSuchElementException("Vector Enumeration");
        }
    };
}
复制代码


#LeetCode# Binary Tree Level Order Traversal

Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level).
For example:
Given binary tree {3,9,20,#,#,15,7},
    3
   / \
  9  20
    /  \
   15   7
return its level order traversal as:
[
  [3],
  [9,20],
  [15,7]
]


这是一道很简单的BFS题,思路看一下题目立刻就有了,但是编码的过程中遇到了不少问题。

给定一个树,按照层续遍历,输出每一层的节点。当然要用队列来实现了!值得注意的是,怎么控制层数呢?

巧妙地引入两个list,current和next,分别代表当前层和下一层,当current当前层遍历完毕后,current<--next,再将next层重新定义。当然,只用一个list也可以实现,我的思路是每次将当前层数和node都加入队列。

实现起来的时候,我遇到了好几个问题,

1. Incompatible types List of List and ArrayList of ArrayList
2. Type mismatch: cannot convert from ArrayList to List
3.  ArrayList<ArrayList<Integer>> cannot be converted to List<List<Integer>>
4. input[1] expected [[1]],我的代码却output [],发现忘了加“!”, while(!current.isEmpty())

最终借助stackoverflow都解决了,这是一个疑难点,我会专门写文章归纳,这里简单用代码归纳下,就是,

        List<List<Integer>> result = new ArrayList<List<Integer>> ();
        ArrayList<Integer> nodeValues = new ArrayList<Integer> ();
        result.add(nodeValues);

好了,上代码吧,DEBUG了一个小时的代码.....

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        
        List<List<Integer>> result = new ArrayList<List<Integer>> ();
        ArrayList<Integer> nodeValues = new ArrayList<Integer> ();
        
        if(root == null) {
            return result;
        }
        
        LinkedList<TreeNode> current = new LinkedList<TreeNode>();
        LinkedList<TreeNode> next = new LinkedList<TreeNode>();
        current.add(root);
        
        while(!current.isEmpty()) {
            TreeNode node = current.remove();
            nodeValues.add(node.val);
            
            if(node.left != null) {
                next.add(node.left);
            }
            if(node.right !=null) {
                next.add(node.right);
            }
            
            if(current.isEmpty()) {
            current = next;
            next = new LinkedList<TreeNode> ();
            result.add(nodeValues);
            nodeValues = new ArrayList ();
            }
        }
        
        return result;
}
}





********************分割线,错误代码********************

//这段代码就是刚开始错误重重不能通过的代码,放在这里以供日后比较。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public ArrayList<ArrayList<Integer>> levelOrder(TreeNode root) {
        
        ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>> ();//错误
        ArrayList<Integer> nodeValues = new ArrayList<Integer> ();  //错误
        
        if(root == null) {
            return result;
        }
        
        LinkedList<TreeNode> current = new LinkedList<TreeNode>();
        LinkedList<TreeNode> next = new LinkedList<TreeNode>();
        current.add(root);
        
        while(current.isEmpty()) {   //应该为不为空的时候
            TreeNode node = current.remove();
            
            if(node.left != null) {
                next.add(node.left);
            }
            if(node.right !=null) {
                next.add(node.right);
            }
            
            nodeValues.add(node.val);
            
            if(current.isEmpty()) {
            result.add(nodeValues);
            current = next;
            next = new LikedList<TreeNode> ();
            nodeValues = new ArrayList<Integer> ();
            }
        }
        
        return result;
}
}