Thuật toán Heap Sort (Sắp xếp vun đống)

Giới thiệu Heap

Trong khoa học máy tính, Heap là một cấu trúc dữ liệu dựa trên cây thỏa mãn tính chất đống, là một trường hợp đặc biệt của cây nhị phân cân bằng thỏa mãn: nếu B là nút con của A thì giá trị khóa của (A) ≥ giá trị khóa của (B). Một hệ quả của tính chất này là giá trị khóa lớn nhất luôn nằm ở nút gốc. Do đó một Heap  như vậy thường được gọi là Max Heap. Nếu mọi phép so sánh bị đảo ngược khiến cho khóa có giá trị nhỏ nhất luôn nằm ở nút gốc thì đống đó gọi là min heap. Heap có vai trò quan trọng trong nhiều thuật toán cho đồ thị chẳng hạn như thuật toán Dijkstra, hay thuật toán sắp xếp Heap Sort.

Thuật toán Heap Sort (Sắp xếp vun đống)

 

Link Video: Giới thiệu heap (Introduction)

Để hiểu rõ hơn về cấu trúc heap các bạn có thể tham khảo thêm nguồn tài liệu sau:

Linh Page: Giới thiệu heap ( Introduction to heap)

 

Xóa nút gốc

Mặc dù tương tự như cây nhị phân cân bằng nhưng Heap có những ràng buộc riêng của nó, vậy làm thế nào để duy trì các ràng buộc này khi chúng ta cần thêm vào / xóa bỏ một phần tử trên Heap. Các bạn hãy xem video này để xem thao tác khi xóa đi một phần tử trên heap 

Link Video: Xóa nút gốc (Deleting the root)

 

Chèn 1 phần tử vào heap

Các bạn hãy xem thao tác khi chèn thêm một phần tử trong heap thông qua video này

Link Video: Chèn một phần tử vào heap ( Inserting an item in a heap)

 

Sử dụng heap để xây dựng hàng đợi ưu tiên

Sử dụng heap để xây dựng hàng đợi ưu tiên (phần tử có độ ưu tiên cao hơn được ra trước, không tuân theo FIFO). Video sẽ mô tả điều này

Link Video: Sử dụng heap xây dựng hàng đợi ưu tiên ( Heaps as Priority Queues)

 

Biểu diễn heap bằng mảng

Một Heap hoàn toàn có thể được biểu diễn bằng mảng 1 chiều, sao cho thứ tự lưu trữ của các item trong mảng tương ứng với cây nhị phân là từ root - > left, từ left children -> right children, các node trên cây khi đặt vào mảng theo tiêu chí sau:

Root có index  i =  0 (node root trong cây khi đặt vào mảng là phần tử đầu tiên trong mảng)

Parent (i) = i/2 (i/2 sẽ là chỉ số node cha của node có chỉ số i ở trong mảng)

Left(i) = 2i ( 2i sẽ là chỉ số node con trái của node có chỉ số i ở trong mảng)

Right(i) = 2i+1 (2i+1 sẽ là chỉ số node con trái của node có chỉ số i ở trong mảng)

Link Video: Biểu diễn heap bằng mảng (repsenting heaps using arays)

 

Thuật toán sắp xếp vun đống (Heap Sort)

Thuật toán Heap Sort vận hành dựa trên cấu trúc Heap.

 

Link video: Thuật toán sắp xếp vun đống (Heap Sort) 

 

Xây dựng một heap

Video sau sẽ mô tả việc xây dựng một heap thông qua ví dụ

Link Video: Xây dựng một heap (building a heap)

 

Cài đặt code bằng Java cho heap sort

import java.util.*;
class EffSort
 {int [] a; int n;
  EffSort() {}
  EffSort(int [] b)
   {int i;
    n = b.length;
    a = new int[n];
    for(i=0;i<n;i++) a[i]= b[i];
   }

// Set dữ liệu cho mảng
  void setData(int [] b)
   {int i;
    n = b.length;
    a = new int[n];
    for(i=0;i<n;i++)
       a[i]= b[i];
   }
  void display()
   {int i;
    for(i=0;i<n;i++)
      System.out.print("  " + a[i]);
    System.out.println();
   }
//==================================================
  /*Sap xep bang HEAP: Heap la cay nhi phan gan day duoc cai dat bang
    mang mot chieu voi cac nut tren heap co noi dung nho hon nut goc
    va cac nhanh cay con cung la cac heap
  */
  void heapSort()
   {/*Chuyen danh sach thanh HEAP bang cach xem khoi dau heap gom nut
      a[0], sau do lan luot chuyen cac nut a[1],a[2],...,
      a[n-1] vao heap*/
    int i,s,f;int x;
    for(i=1;i<n;i++)
     {x=a[i];//Nut can them vao HEAP, ban dau heap co mot nut a[0].
          /*Insert x vao vi tri thich hop cua HEAP: xuat phat tu i, di dan ve
            goc de tim mot vi tri nut cha thich hop. Vay f se giam dan
          */
      s=i; //s la nut con, hien tai tren heap chua co a[i]
       //f=(s-1)/2 la nut cha
      while(s>0 && x>a[(s-1)/2])
        {a[s]=a[(s-1)/2]; //Keo nut < x xuong 1 muc
         s=(s-1)/2;
        };
      a[s]=x; //Dua nut x vao vi tri phu hop
     };
    //Ket thuc chuyen danh sach thanh heap
    System.out.println(" Heap:");
    display();
    //Lan luot xoa nut a[0] tren HEAP, dua ve vi tri thich hop tren ds
    for(i=n-1;i>0;i--)
     {x=a[i];a[i]=a[0];
      /*O buoc i heap co i+1 nut, la a[0], a[1],...,a[i]
        Muc dich cua chung ta la dua nut a[0] ve vi tri a[i],
        dong thoi, chen a[i] vao heap sao cho cau truc heap van bao
        toan. De lam dieu nay ta bat dau tu
        nut f = 0, theo con duong cha - con trai hoac phai, tim mot vi tri
        de chen nut a[i]. De co duoc nut trong de chen a[i], ta can
        dich cac nut tren duong di len mot muc, bang cong thuc
        nutgoc=max(contrai,conphai,a[i])
      */
      f=0; //f la nut cha, s la nut con lon hon
      s=2*f+1; //Gan s la nut con ben trai
      if(s+1<i && a[s]<a[s+1]) s=s+1;/*Neu co nut phai va
     lon hon thi chon nut phai*/
      while(s<i && x<a[s])
       {a[f]=a[s];//Con lon thay the cha
 f=s;//Chuyen den con lon tiep theo
 s=2*f+1;
 if(s+1<i && a[s]<a[s+1]) s=s+1;
       };
       a[f]=x; //Nut a[k] duoc chen dung cho
     };
   };
 }
//==================================================
public class Main
 {public static void main(String args[])
   {int [] b = {7,3,5,9,11,8,6,15,10,12,14};
    EffSort t = new EffSort(b);
    t.heapSort();t.display();
    System.out.println();
   }
 }

 

**************************

Bài tập thực hành

 

Hãy viết chương trình cài đặt một  HeapSort  bằng mảng, sau đó hãy hiển thị tất cả các phần tử trong HeapSort ra màn hình. Giả sử rằng các phần tử đầu vào của Heap sort là mảng a[]= {7,3,5,9,11,8,6,15,10,12,14}.

Gợi ý:

- Gợi ý về Input, Output:

Input:

   Mảng a với các giá trị cụ thể:

        a[]=7,3,5,9,11,8,6,15,10,12,14;

Output:     3  5  6  7  8  9  10  11  12  14  15

- Chương trình gồm có các lớp và các hàm cơ bản sau, các bạn hoàn toàn có thể thêm lớp, các hàm nếu cảm thấy cần thiết hoặc có thể làm bằng cách khác

class EffSort
 {int [] a; int n;
  EffSort() {}
  EffSort(int [] b)
   {int i;
    n = b.length;
    a = new int[n];
    for(i=0;i<n;i++) a[i]= b[i];
   }

  void display()
   {int i;
    for(i=0;i<n;i++)
      System.out.print("  " + a[i]);
    System.out.println();
   }
//==================================================
  /*Sap xep bang HEAP: Heap la cay nhi phan gan day duoc cai dat bang
    mang mot chieu voi cac nut tren heap co noi dung nho hon nut goc
    va cac nhanh cay con cung la cac heap
  */
  void heapSort()
   {…………..
 }}
//==================================================
public class Main
 {public static void main(String args[])
   {int [] b = {7,3,5,9,11,8,6,15,10,12,14};
    EffSort t = new EffSort(b);
    t.heapSort();t.display();
    System.out.println();
   }
 }

 

 

Giới thiệu các thuật toán sắp xếp nâng cao

Giới thiệu các thuật toán sắp xếp nâng cao trong cấu trúc dữ liệu và giải thuật

Mục tiêu của bài học này là cung cấp kiền thức về các thuật toán sắp xếp nâng cao như:

- Quick Sort

- Shell Sort

- Counting Sort

- Radix Sort

- Merge Sort



Giới thiệu cơ bản thuật toán sắp xếp nhanh (Quick Sort)

 

Thuật toán là quick sort một thuật toán sắp xếp nhanh (phát triển bởi C.A.R. Hoare) trên một danh sách cho trước, gồm hai giai đoạn, giai đoạn 1, chia nó thành hai danh sách bằng cách so sánh từng phần tử của danh sách với một phần tử được chọn (được gọi là phần tử chốt). Những phần tử nhỏ hơn hoặc bằng phần tử chốt được đưa về phía trước và nằm trong danh sách con thứ nhất, các phần tử lớn hơn chốt được đưa về phía sau và thuộc danh sách đứng sau. Cứ tiếp tục chia như vậy tới khi các danh sách con đều có độ dài bằng 1

Phần này giới thiệu  kiến thức cơ bản của về thuật toán quick sort

Link Page: https://www.tutorialspoint.com/data_structures_algorithms/quick_sort_algorithm.htm

Thuật toán quick sort là thuật toán chia để trị, bước chia (partition) cũng là một bước dài và khó để tìm trụ (pivot), bước trị chỉ là việc đệ quy trên phần nhỏ hơn của mảng dựa trên giá trị pivot

Video này trình bày, giải thích về thuật toán quick sort thông qua giả mã và ví dụ cụ thể.

Link Video: Giới thiệu Quick Sort (Quick Sort)

Video này tiếp tục giải thích về bước partition trong thuật toán quick sort  thông qua giả mã và ví dụ cụ thể.

Link Video:

https://www.udemy.com/introduction-to-data-structures-algorithms-in-java/learn/v4/t/lecture/805552

 

Giới thiệu cơ bản về thuật toán Shell Sort

Thuật toán Shell Sort là phiên bản nâng cấp của Insertion Sort, ý tưởng chính của thuật toán là phân chia dãy ban đầu thành những dãy con mà mỗi phần tử của dãy cách nhau 1 vị trí là h. Sử dụng Insertion sort áp dụng trên mỗi dãy con đó, sẽ làm cho các phần tử được đưa về vị trí đúng tương đối (trong dãy con) 1 cách nhanh chóng.

Sau đó tiếp tục giảm khoảng cách h để tạo thành các dãy con mới (Tạo điều kiện để so sánh một phần tử với nhiều phần tử khác trước đó không ở cùng dãy con với nó) và lại tiếp tục sắp xếp.

Thuật toán dừng khi h = 1, lúc này bảo đảm tất cả các phần tử trong dãy ban đầu sẽ được so sánh với nhau để xác định trật tự cuối cùng.

Nguồn tài liệu này sẽ giới thiệu cơ bản về thuật toán  Shell Sort

Link Page: https://www.tutorialspoint.com/data_structures_algorithms/shell_sort_algorithm.htm

 

Video giới thiệu thuật toán shell sort được nâng cấp từ thuật toán insertion sort thông qua ví dụ.

Link Video: Thuật toán Shell Sort (shell sort)

 

Cài đặt Shell Sort trên JAVA

class EffSort
 {int [] a; int n;
  EffSort() {}
  EffSort(int [] b)
   {int i;
    n = b.length;
    a = new int[n];
    for(i=0;i<n;i++) a[i]= b[i];
   }

// Set giá trị cho mảng
  void setData(int [] b)
   {int i;
    n = b.length;
    a = new int[n];
    for(i=0;i<n;i++)
       a[i]= b[i];
   }

// Hiển thị giá trị trong mảng
  void display()
   {int i;
    for(i=0;i<n;i++)
      System.out.print("  " + a[i]);
    System.out.println();
   }
//==================================================
  // shell sort
  void insertSort2(int h)
   {int i,j,x;
    for(i=h;i<n;i++)
      {x=a[i];
       j=i;
       while(j-h>=0 && x<a[j-h])
         {a[j]=a[j-h];//Keo nut lon hon x len h vi tri
          j=j-h;
         };
       a[j]=x;
      }
   };
  void shellSort(int [] step)
   { /*Cac buoc tang la step[0],step[1],...,step[stepnum] trong do
       step[0]>step[1]>...>step[stepnum]  */
    int h,i,stepnum;
    stepnum = step.length;
    for(i=0;i<stepnum;i++)
     {h=step[i];
      insertSort2(h);
     }
   };
  void shellSort()
   {int [] step = {5,3,1};
    shellSort(step);
   };
 }
//==================================================
public class Main
 {public static void main(String args[])
   {int [] b = {7,3,5,9,11,8,6,15,10,12,14};
    EffSort t = new EffSort(b);
    t.shellSort();t.display();
    System.out.println();
   }
 }

Thuật toán sắp xếp đếm (Counting Sort)

Thuật toán Counting Sort: Counting Sort là thuật toán đếm số lần xuất hiện của các giá trị khác nhau trong mảng ban đầu và gán giá trị vừa đếm vào vị trí tương ứng của 1 mảng có độ dài là giá trị lớn nhất của mảng.

Đây là hàm code bằng java trình bày thuật toán

int[] countingSort(int[] a, int k) {
        int c[] = new int[k];
        for (int i = 0; i < a.length; i++)
            c[a[i]]++;
        for (int i = 1; i < k; i++)
            c[i] += c[i-1];
        int b[] = new int[a.length];
        for (int i = a.length-1; i >= 0; i--)
            b[--c[a[i]]] = a[i];
        return b;
    }

Video giới thiệu thuật toán Counting sort

Link video: Thuật toán sắp xếp đếm (Counting Sort)

 

Thuật toán sắp xếp theo cơ số (Radix Sort)

Radix Sort dựa trên tính chất "số" của các khóa. Trong giải thuật sắp xếp theo cơ số, ta không chỉ so sánh giá trị của các khóa mà so sánh các thành phần của khóa. Giả sử các khóa là các số biểu diễn theo hệ ghi số cơ số M. Khi đó sắp xếp theo cơ số sẽ so sánh từng k.

Chúng ta mô tả cách sắp này khi cơ số M=10. Giả sử phải sắp các hồ sơ đánh số bởi 3 chữ số thập phân. Đầu tiên ta chia các hồ sơ vào các đống có cùng chữ số hàng trăm (đồng thời xếp các đống theo thứ tự của chữ số hàng trăm), trong mỗi đống con lại phân chia theo chữ số hàng chục, cuối cùng trong mỗi đống có cùng chữ số hàng trăm và hàng chục, sắp xếp theo thứ tự của chữ số hàng đơn vị.

Trong máy tính, đương nhiên việc sắp xếp theo cơ số nhị phân (cơ số 2) hoặc cơ số là lũy thừa của 2 là thuận lợi nhất. Trong trường hợp cơ số 2, việc phân hoạch tương tự như phân hoạch trong Quick Sort, chỉ khác ở chỗ cách chọn phần tử chốt.

Radix Sort hoàn toàn có thể được áp dụng để sắp xếp string.

Video giới thiệu về thuật toán Radix Sort thông qua ví dụ

Link Video: Thuật toán sắp xếp theo cơ số (Radix Sort)

 

Cài đặt Radix Sort in JAVA 

/****************************************************************************

 

 * Author: Isai Damier
 * Title: Radix Sort
 * Project: geekviewpoint
 * Package: algorithms
 *
 * Statement:
 *   Given a disordered list of integers, rearrange them in natural order.
 *
 * Sample Input: {18,5,100,3,1,19,6,0,7,4,2}
 *
 * Sample Output: {0,1,2,3,4,5,6,7,18,19,100}
 *
 * Time Complexity of Solution:
 *   Best Case O(k*n); Average Case O(k*n); Worst Case O(k*n), 
 *   where k is the length of the longest number and n is the 
 *   size of the input array.
 * 
 *   Note: if k is greater than log(n) then an n*log(n) algorithm would be a
 *         better fit. In reality we can always change the radix to make k
 *         less than log(n).
 * 
 * Approach:

 

 *   radix sort, like counting sort and bucket sort, is an integer based
 *   algorithm (i.e. the values of the input array are assumed to be
 *   integers). Hence radix sort is among the fastest sorting algorithms 
 *   around, in theory. The particular distinction for radix sort is that it
 *   creates a bucket for each cipher (i.e. digit); as such, similar to
 *   bucket sort, each bucket in radix sort must be a growable list that may
 *   admit different keys.
 *
 *   For decimal values, the number of buckets is 10, as the decimal system
 *   has 10 numerals/cyphers (i.e. 0,1,2,3,4,5,6,7,8,9). Then the keys are
 *   continuously sorted by significant digits.

 

 ***************************************************************************/

 

 public void radixsort(int[] input) {
  final int RADIX = 10;
  // declare and initialize bucket[] Khai báo và khởi tạo
  List<Integer>[] bucket = new ArrayList[RADIX];
  for (int i = 0; i < bucket.length; i++) {
    bucket[i] = new ArrayList<Integer>();
  } 
  // sort
  boolean maxLength = false;
  int tmp = -1, placement = 1;
  while (!maxLength) {
    maxLength = true;
    // split input between lists
    for (Integer i : input) {
      tmp = i / placement; 
      bucket[tmp % RADIX].add(i);
      if (maxLength && tmp > 0) {
        maxLength = false;
      }
    }

 

    // empty lists into input array 
    int a = 0; 
    for (int b = 0; b < RADIX; b++) { 
      for (Integer i : bucket[b]) {
        input[a++] = i;
      }
      bucket[b].clear();
    }
    // move to next digit
    placement *= RADIX;
  }
}

 

Thuật toán sắp xếp trộn (Merge Sort)

Thuật toán Merge Sort cùng với sắp xếp nhanh (quick sort) là hai thuật toán sắp xếp dựa vào tư tưởng "chia để trị" (divide and conquer). Thủ tục cơ bản là việc trộn hai danh sách đã được sắp xếp vào một danh sách mới theo thứ tự. Nó có thể bắt đầu trộn bằng cách so sánh hai phần tử một (chẳng hạn phần tử thứ nhất với phần tử thứ hai, sau đó thứ ba với thứ tư...) và sau khi kết thúc bước 1 nó chuyển sang bước 2. Ở bước 2 nó trộn các danh sách hai phần tử thành các danh sách bốn phần tử. Cứ như vậy cho đến khi hai danh sách cuối cùng được trộn thành một.

Nguồn tài liệu này sẽ giới thiệu cơ bản về thuật toán merge_sort

Link page: https://www.tutorialspoint.com/data_structures_algorithms/merge_sort_algorithm.htm

Video này giới thiệu về thuật toán merge_sort thông qua ví dụ

Link Video: Sắp xếp hợp nhất (Merge sort) 

 

Cài đặt Merge Sort trên JAVA 

Ngoài ra các bạn có thể xem code Merge sort in java

import java.util.*;
class EffSort
 {int [] a; int n;
  EffSort() {}
  EffSort(int [] b)
   {int i;
    n = b.length;
    a = new int[n];
    for(i=0;i<n;i++) a[i]= b[i];
   }
  void setData(int [] b)
   {int i;
    n = b.length;
    a = new int[n];
    for(i=0;i<n;i++)
       a[i]= b[i];
   }
  void display()
   {int i;
    for(i=0;i<n;i++)
      System.out.print("  " + a[i]);
    System.out.println();
   }
//==================================================
  /*Sap xep danh sach theo thu tu tang dan bang phuong phap
    Merge Sort.
    Input:  Day a[0],a[1],...,a[n-1]
    Output: Danh sach da duoc sap xep
   */
  void mergeSort(int p, int r)
    {if(p>=r) return;
      int q = (p+r)/2;
      mergeSort(p,q);
      mergeSort(q+1,r);
      merge(p,q,r);
    }

  void merge(int p, int q, int r)
    {if(!(p<=q) && (q<=r)) return;
      int n,i,j,k,x; n = r-p+1;
      int [] b = new int[n];
      i=p;j=q+1;k=0;
      while(i<=q && j<=r)
        {if(a[i]<a[j])
           b[k++] = a[i++];
            else
             b[k++] = a[j++];
        }
      while(i<=q)  b[k++] = a[i++];
      while(j<=r)  b[k++] = a[j++];
      k=0;
      for(i=p;i<=r;i++) a[i] = b[k++];
    }
 }
//==================================================
public class Main
 {public static void main(String args[])
   {int [] b = {7,3,5,9,11,8,6,15,10,12,14};
    EffSort t = new EffSort(b);
    int n=b.length;
    t.mergeSort(0,n-1);t.display();
    System.out.println();
   }
 }

 

 

******************** 

Bài tập thực hành:

Viết chương trình để nhập vào một mảng a gồm n số nguyên, sau đó hãy sắp xếp các phần tử trong mảng bằng phương pháp quick sort. Tạo menu cho chương trình, menu gồm các chức năng sau:

- Nhập mảng a gồm n số nguyên 

- Sắp xếp mảng a

- Hiển thị giá trị của mảng a ra màn hình

Gợi ý:

- Gợi ý về Input và Output:

Menu sẽ hiển thị như sau:

1. Nhập dữ liệu

2. Sắp xếp 

3. Hiển thị giá trị mảng a 

Nhập vào chức năng 1/2/3:1

input:

n= 11;

a[] = 7,3,5,9,11,8,6,15,10,12,14;

Nhập vào chức năng 1/2/3: 3

Output: 7,3,5,9,11,8,6,15,10,12,14;

Nhập vào chức năng 1/2/3: 2

Nhập vào chức năng 1/2/3: 3

Output: 3  5  6  7  8  9  10  11  12  14  15

- Chương trình gồm có các lớp và các phương thức cơ bản sau (các em có thể thêm các lớp, các phương thức nếu cảm thấy cần thiết) hoặc có thể làm theo cách khác

// Lớp EffSort

class EffSort
 {int [] a; int n;
  EffSort() {}
  EffSort(int [] b)
   {int i;
    n = b.length;
    a = new int[n];
    for(i=0;i<n;i++) a[i]= b[i];
   }
   void display()
   {int i;
    for(i=0;i<n;i++)
      System.out.print("  " + a[i]);
    System.out.println();
   }
//--------------------------------------------------
  void swap(int [] a, int i, int k) // Hàm hoán vị
   {int x; x=a[i];a[i]=a[k];a[k]=x;
   }
//==================================================
  /*CAI DAT GIAI THUAT QUICKSORT}
    Phương thức partition sẽ phân hoạch danh sách từ low đến up thành 2 phần:
    các nút có nội dung <= a[pivot] nằm bên trái pivot, các nútt có nội dung > a[pivot] nằm bên phải
    Chọn nút trục=a[low], quét 2 đầu, i từ dưới lên, j trên xuống và đổi chỗ các phần tử sai chỗ, khi kết thúc quét thì đổi chỗ cho a[low]
    và a[j], vậy nút trục sẽ chuyển đến vị trí j;*/


  int partition(int low,int up) // return pivot index
   {……………………...
   }
  // Phương thức quicksort sẽ gọi tới phương thức partition để sắp xếp theo thuật toán quick sort
  void quickSort(int low,int up)
   {………………………………...
   }
  //-----------------------------------------------------
  void quickSort()
   {quickSort(0,n-1);
   }
 }
//==================================================
public class Main{

static int [] b;

// Phương thức input để nhập giá trị cho mảng b;

static void input(int [] b)

{…………………….}

/*Trong phương thức main phải xây dựng menu để gọi tới các phương thức cần thiết như input(b), gọi tới phương thức khởi tạo  EffSort(b) và  quickSort(); display() trong lớp EffSort*/
 public static void main(String args[])
   {

……………………………………….


   }
 }

[Giải thuật đồ thị] Tìm hiểu các giải thuật đồ thị qua các ví dụ

Tìm hiểu các giải thuật đồ thị qua các ví dụ

Theo Wikipedia

Trong toán học và tin học, lý thuyết đồ thị (tiếng Anh: graph theory) nghiên cứu các tính chất của đồ thị. Một cách không chính thức, đồ thị là một tập các đối tượng được gọi là các đỉnh (hoặc nút) nối với nhau bởi các cạnh (hoặc cung). Cạnh có thể có hướng hoặc vô hướng. Đồ thị thường được vẽ dưới dạng một tập các điểm (các đỉnh nối với nhau bằng các đoạn thẳng (các cạnh).

Đồ thị biểu diễn được rất nhiều cấu trúc, nhiều bài toán thực tế có thể được biểu diễn bằng đồ thị. Ví dụ, cấu trúc liên kết của một website có thể được biểu diễn bằng một đồ thị có hướng như sau: các đỉnh là các trang web hiện có tại website, tồn tại một cạnh có hướng nối từ trang A tới trang B khi và chỉ khi A có chứa 1 liên kết tới B. Do vậy, sự phát triển của các thuật toán xử lý đồ thị là một trong các mối quan tâm chính của khoa học máy tính.

Lý thuyết đồ thị


Cấu trúc đồ thị có thể được mở rộng bằng cách gán trọng số cho mỗi cạnh. Có thể sử dụng đồ thị có trọng số để biểu diễn nhiều khái niệm khác nhau. Ví dụ, nếu đồ thị biểu diễn một mạng đường giao thông, các trọng số có thể là độ dài của mỗi con đường. Một cách khác để mở rộng đồ thị cơ bản là quy định hướng cho các cạnh của đồ thị (như đối với các trang web, A liên kết tới B, nhưng B không nhất thiết cũng liên kết tới A). Loại đồ thị này được gọi là đồ thị có hướng. Một đồ thị có hướng với các cạnh có trọng số được gọi là một lưới.

Các lưới có nhiều ứng dụng trong khía cạnh thực tiễn của lý thuyết đồ thị, chẳng hạn, phân tích lưới có thể dùng để mô hình hoá và phân tích mạng lưới giao thông hoặc nhằm "phát hiện" hình dáng của Internet - (Xem thêm các ứng dụng đưới đây. Mặc dù vậy, cũng nên lưu ý rằng trong phân tích lưới, thì định nghĩa của khái niệm "lưới" có thể khác nhau và thường được chỉ ra bằng một đồ thị đơn giản.)

Lý thuyết đồ thị nói riêng, toán rời rạc nói chung là nội dung không thể thiếu trong chương trình đào tạo của kỹ sư ngành CNTT. Học phần này sẽ giúp sinh viên hình thành được kỹ năng giải quyết bài toán một cách tối ưu thông qua các thuật toán kinh điển trong toán học, đặc biệt là các thuật toán tìm đường đi ngắn nhất.

Đã có rất nhiều ứng dụng của lý thuyết này trong các lĩnh vực khác nhau như: bài toán vấn tải, bài toán người du lịch, bài toán tìm đường của gói tin trên Internet, bài toán tối ưu, các bài toán sinh học, di truyền, ...

Cách tốt nhất để làm chủ các thuật toán về đồ thị là cài đặt và chạy thử nó trên một bài tóan cụ thể nào đó. Trong bài viết này, tôi sẽ mang đến cho bạn một số ví dụ cài đặt thuật toán quan trọng trong lý thuyết đồ thị.

Ví dụ cài đặt thuật toán đồ thị:



1.  [THUẬT TOÁN ĐỒ THỊ / CODE C++] TÌM SỐ THÀNH PHẦN LIÊN THÔNG CỦA ĐỒ THỊ G


2.  [THUẬT TOÁN ĐỒ THỊ / CODE C++] THUẬT TOÁN KRUSKAL TÌM CÂY KHUNG NHỎ NHẤT CỦA ĐỒ THỊ G


3. [THUẬT TOÁN ĐỒ THỊ / CODE C++] THUẬT TOÁN PRIM TÌM CÂY KHUNG NHỎ NHẤT CỦA ĐỒ THỊ G


4. [THUẬT TOÁN ĐỒ THỊ / CODE C++] THUẬT TOÁN DIJKSTRA TÌM ĐƯỜNG ĐI NGẮN NHẤT TRÊN ĐỒ THỊ G


5. [THUẬT TOÁN ĐỒ THỊ / CODE C++] THUẬT TOÁN ELUER - TÌM CHU TRÌNH ELUER TRÊN ĐỒ THỊ G


6. [THUẬT TOÁN ĐỒ THỊ / CODE C++] THUẬT TOÁN HAMILTON - TÌM CHU TRÌNH HAMILTON TRÊN ĐỒ THỊ G


7. [THUẬT TOÁN ĐỒ THỊ / CODE C++] THUẬT TOÁN EULER - TÌM ĐƯỜNG ĐI EULER TRÊN ĐỒ THỊ G (VỚI ĐỒ THỊ NỬA EULER)


8. [THUẬT TOÁN ĐỒ THỊ / CODE C++] THUẬT TOÁN DIJKSTRA - TÌM ĐƯỜNG ĐI NGẮN NHẤT TỪ ĐỈNH D ĐẾN ĐỈNH C TRÊN ĐỒ THỊ G.


9. [C\C++] CÀI ĐẶT THUẬT TOÁN DUYỆT ĐỒ THỊ (DFS, BFS) [LÝ THUYẾT ĐỒ THỊ]


10. [THUẬT TOÁN ĐỒ THỊ / CODE C++] KIỂM TRA TÍNH LIÊN THÔNG CỦA ĐỒ THỊ G


Tài liệu tham khảo:


1. Bài giảng: Toán rời rạc - PTIT   [Download]

2. Tài liệu: Giải thuật đồ thị - ĐHQG  [Download]

3. Giáo trình: Toán rời rạc  [Download]



[Kỹ thuật đồ họa] Học kỹ thuật đồ họa qua các ví dụ

Học kỹ thuật đồ họa qua các ví dụ

Đồ họa máy tính là một lĩnh vực của khoa học máy tính nghiên cứu về cơ sở toán học, các thuật toán cũng như các kĩ thuật để cho phép tạo, hiển thị và điều khiển hình ảnh trên màn hình máy tính. Đồ họa máy tính có liên quan ít nhiều đến một số lĩnh vực như đại số, hình học giải tích, hình học họa hình, quang học,... và kĩ thuật máy tính, đặc biệt là chế tạo phần cứng (các loại màn hình, các thiết bị xuất, nhập, các vỉ mạch đồ họa...).

[Kỹ thuật đồ họa] Học kỹ thuật đồ họa qua các ví dụ


Theo nghĩa rộng hơn, đồ họa máy tính là phương pháp và công nghệ dùng trong việc chuyển đổi qua lại giữa dữ liệu và hình ảnh trên màn hình bằng máy tính. Đồ họa máy tính hay kĩ thuật đồ họa máy tính còn được hiểu dưới dạng phương pháp và kĩ thuật tạo hình ảnh từ các mô hình toán học mô tả các đối tượng hay dữ liệu lấy được từ các đối tượng trong thực tế. Thuật ngữ "đồ họa máy tính" (computer graphics) được đề xuất bởi một chuyên gia người Mĩ tên là William Fetter vào năm 1960. Khi đó ông đang nghiên cứu xây dựng mô hình buồng lái máy bay cho hãng Boeing. William Fetter đã dựa trên các hình ảnh 3 chiều của mô hình người phi công trong buồng lái để xây dựng nên mô hình buồng lái tối ưu cho máy bay Boeing. Đây là phương pháp nghiên cứu rất mới vào thời kì đó. Phương pháp này cho phép các nhà thiết kế quan sát một cách trực quan vị trí của người lái trong khoang buồng lái. William Fetter đã đặt tên cho phương pháp của mình là computer graphics... 

Theo wikipedia.

Đồ họa máy tính cũng là môn học bắt buộc đối với sinh viên khối ngành CNTT nói chung. Môn học giúp cho sinh viên có cái nhìn tổng quan về các kỹ thuật đồ họa, các thuật toán đồ họa cơ bản từ đó hình thành tư duy thiết kế, kiến tọa đồ họa trên máy tính. Đô họa máy tính được ứng dụng trong rất nhiều lĩnh vực khác nhau, như kiến tạo đồ họa, xử lý đồ họa, các công nghệ mô phỏng, giả lập, các trò chơi điện tử, điện ảnh và du lịch.

Tuy nhiên để học tốt học phần này sinh viên cần có nền tảng lập trình vững vàng, có kỹ năng đọc hiểu và thiết kế thuật toán cho máy tính. Bài viết này mong muốn mang đến cho các bạn một cách tiếp cần môn học thông qua các ví dụ từ cơ bản đến nâng cao, giúp cho các bạn hiểu kỹ hơn về tư duy kiến tạo đồ họa trong máy tính.

Một số ví dụ tham khảo (sử dụng C/C++)

1. [ KỸ THUẬT ĐỒ HỌA MÁY TÍNH ] VÍ DỤ VẼ TAM GIÁC, SỬ DỤNG THUẬT TOÁN VẼ ĐOẠN THẲNG THÔNG THƯỜNG [ C\C++ ]


2. [ C\C++ ] THUẬT TOÁN DDA_LINE - VẼ ĐOẠN THẲNG [ KỸ THUẬT ĐỒ HỌA MÁY TÍNH ]

8. [ĐỒ HỌA TRONG C\C++] SỬ DỤNG CÁC HÀM ĐỒ HỌA THÔNG DỤNG TRONG C\C++ [C\C++]


9. BÀI TẬP +ĐÁP ÁN LÝ THUYẾT ĐỒ HỌA MÁY TÍNH
 

Tài liệu tham khảo:


1. Bài giảng: Kỹ thuật đồ họa máy tính - PTIT  [Donwload]

2. Tài liệu: Lý thuyết đồ họa  [Donwload]

3. Tài liệu: Kỹ thuật đồ họa  [Donwload]

4. Slide: Thuật toán trong đồ họa  [Donwload]



#

[Tự học Android] Sử dụng Shared Preferences trong Android

Sử dụng Shared Preferences trong Android

 

Trong bài tập này Tôi sẽ trình bày về cách lưu trạng thái của ứng dụng (liên quan tới Shared Preferences) và cách tạo các màn hình cấu hình (liên quan tới Shared Preference Change Listeners)

———————————————————


*******

Một số tài liệu và khoá học bổ ích dành cho bạn: 

# Giáo trình: Lập Trình Android [Click để xem]

# Khoá học online:  Lập trình Android toàn tập [Click để xem]


-A- Cách lưu trạng thái của ứng dụng:

Bước 1:

– Gọi hàm getSharedPreferences, hàm này trả về SharedPreferences và nó có 2 đối. Đối số 1 là tên tập tin để lưu trạng thái, đối số 2 là kiểu lưu. Chú ý là đối số 1 ta chỉ ghi tên tập tin (không cần ghi phần mở rộng, vì phần mở rộng mặc nhiên của nó là .xml khi ta lưu thành công), đối số 2 thường ta để là MODE_PRIVATE:

SharedPreferences pre=getSharedPreferences(“my_data“, MODE_PRIVATE);

Bước 2:

– Tạo đổi tượng Editor để cho phép chỉnh sửa dữ liệu:

SharedPreferences.Editor edit=pre.edit();

Bước 3:

– Đưa dữ liệu muốn lưu trữ vào edit bằng các phương thức edit.putXXX(“key”,”value”);

Tùy vào kiểu dữ liệu ta muốn lưu trữ mà XXX được thay thế bởi các kiểu dữ liệu phù hợp:

editor.putString(“user”, “drthanh”);

editor.putString(“pwd”, “hoilamgi”);

editor.putBoolean(“checked”, true);

Bước 4:

– Lưu trạng thái bằng cách gọi dòng lệnh:

editor.commit();

————————————————————————————————————————–

Sau khi bạn làm đúng 4 bước trên thì chương trình sẽ lưu được trạng thái, như trên đã nói mặc định phần mở rộng là .xml (tức là trạng thái được lưu dưới định dạng tập tin XML). Bên trên ta đặt tên là my_data có nghĩa là chương trình sẽ tạo ra tập tin my_data.xml (bạn mở DDMS lên để xem) – tự động nó sẽ lưu vào thư mục shared_prefs như hình bên dưới:

30_SharedPreferences_5

————————————————————————————————————————–

-B- Cách đọc trạng thái đã lưu:

Rất đơn giản

Bước 1:

– Gọi hàm getSharedPreferences để trả về đối tượng SharedPreferences (giống như phần lưu trạng thái mà Tôi nói ở trên)

SharedPreferences pre=getSharedPreferences (“my_data“,MODE_PRIVATE);

Bước 2:

– Gọi các phương thức getXXX(“key”,giá trị mặc định) để lấy các giá trị lúc trước được lưu

boolean bchk=pre.getBoolean(“checked”, false); //đối số 2 Tôi để false là giá trị mặc định khi nó tìm không thấy key =checked
String user=pre.getString(“user”, “”);//lấy giá trị được lưu trong key=user, nếu không thấy thì gán giá trị mặc định là chuỗi rỗng
String pwd=pre.getString(“pwd”, “”);//giống trên

————————————————————————————————————————–

******** Gợi ý *******

– Lưu và đọc trạng thái bạn nên viết thành các hàm riêng biệt cho dễ sử dụng

– Hàm lưu bạn gọi trong sự kiện onPause

– Hàm đọc bạn gọi trong sự kiện onResume.

******** END *******

Tôi sẽ làm ví dụ cụ thể dưới đây để bạn hiểu rõ hơn về lý thuyết:

Ví dụ 1: bạn muốn tạo một màn hình đăng nhập có checkbox cho phép lưu lại thông tin đăng nhập, lần sau khởi đội lại thì nó sẽ lấy lại thông tin nhập lúc trước để người sử dụng đỡ phải mất công nhập lại (biết về Shared Preferences), xem hình mình họa:

30_SharedPreferences_0

– Khi chọn nút đăng nhập, chương trình sẽ đóng Activity hiện tại và hiển thị Activity dưới đây:

30_SharedPreferences_2

– Chọn nút Thoát, chương trình tiếp tục đóng Activity này giúp tắt hẳn các Activity trong ứng dụng.

– Khởi động lại chương trình sẽ phải tự động load lại thông tin đăng nhập trước đó (Nếu khi đăng nhập có chọn “Lưu thông tin“).

– Bạn xem cấu trúc của Project:

30_SharedPreferences_4

– Layout XML của màn hình chính (activity_main.xml):

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/LinearLayout1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context=".MainActivity" >
 
<TextView
 android:id="@+id/textView1"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:background="#80FFFF"
 android:gravity="center"
 android:text="Đăng nhập hệ thống" />
 
<TableLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:stretchColumns="*"
 >
 
<TableRow
 android:id="@+id/tableRow1"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" >
 
<TextView
 android:id="@+id/textView2"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="User:" />
 
<EditText
 android:id="@+id/editUser"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:inputType="text"
 android:ems="10" >
 
<requestFocus />
 </EditText>
 </TableRow>
 
<TableRow
 android:id="@+id/tableRow2"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" >
 </TableRow>
 
<TableRow
 android:id="@+id/tableRow3"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" >
 
<TextView
 android:id="@+id/textView3"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="Pasword:" />
 
<EditText
 android:id="@+id/editPassword"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:ems="10"
 android:inputType="textPassword" />
 </TableRow>
 
<TableRow
 android:id="@+id/tableRow4"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" >
 
<CheckBox
 android:id="@+id/chksaveacount"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_column="1"
 android:text="Lưu thông tin" />
 </TableRow>
 
<TableRow
 android:id="@+id/tableRow5"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" >
 
<Button
 android:id="@+id/btnlogin"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_column="1"
 android:text="Đăng nhập" />
 </TableRow>
 </TableLayout>
 
</LinearLayout>

– Layout XML của activity_dang_nhap_thanh_cong.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/LinearLayout1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context=".DangNhapThanhCongActivity" >
 
<TextView
 android:id="@+id/txtmsg"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:text="TextView"
 android:textSize="20sp" />
 
<Button
 android:id="@+id/btnThoat"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_gravity="center"
 android:text="Thoát" />
 
</LinearLayout>

– Bạn xem Source code MainActivity.java:

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package tranduythanh.com;
 
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
 
public class MainActivity extends Activity {
 Button btnlogin;
 EditText edituser,editpassword;
 CheckBox chksaveaccount;
 //đặt tên cho tập tin lưu trạng thái
 String prefname="my_data";
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 btnlogin=(Button) findViewById(R.id.btnlogin);
 edituser =(EditText)
 findViewById(R.id.editUser);
 editpassword=(EditText)
 findViewById(R.id.editPassword);
 chksaveaccount=(CheckBox)
 findViewById(R.id.chksaveacount);
 btnlogin.setOnClickListener(
 new View.OnClickListener() {
 public void onClick(View arg0) {
 doLogin();
 }
 });
 }
 /**
 * hàm đăng nhập hệ thống
 */
 public void doLogin()
 {
 finish();//đóng màn hình hiện tại
 Intent i=new Intent(this, DangNhapThanhCongActivity.class);
 //truyền dữ liệu qua màn hình mới
 i.putExtra("user", edituser.getText().toString());
 startActivity(i);//mở màn hình mới
 }
 @Override
 protected void onPause() {
 // TODO Auto-generated method stub
 super.onPause();
 //gọi hàm lưu trạng thái ở đây
 savingPreferences();
 }
 @Override
 protected void onResume() {
 // TODO Auto-generated method stub
 super.onResume();
 //gọi hàm đọc trạng thái ở đây
 restoringPreferences();
 }
 /**
 * hàm lưu trạng thái
 */
 public void savingPreferences()
 {
 //tạo đối tượng getSharedPreferences
 SharedPreferences pre=getSharedPreferences
 (prefname, MODE_PRIVATE);
 //tạo đối tượng Editor để lưu thay đổi
 SharedPreferences.Editor editor=pre.edit();
 String user=edituser.getText().toString();
 String pwd=editpassword.getText().toString();
 boolean bchk=chksaveaccount.isChecked();
 if(!bchk)
 {
 //xóa mọi lưu trữ trước đó
 editor.clear();
 }
 else
 {
 //lưu vào editor
 editor.putString("user", user);
 editor.putString("pwd", pwd);
 editor.putBoolean("checked", bchk);
 }
 //chấp nhận lưu xuống file
 editor.commit();
 }
 /**
 * hàm đọc trạng thái đã lưu trước đó
 */
 public void restoringPreferences()
 {
 SharedPreferences pre=getSharedPreferences
 (prefname,MODE_PRIVATE);
 //lấy giá trị checked ra, nếu không thấy thì giá trị mặc định là false
 boolean bchk=pre.getBoolean("checked"false);
 if(bchk)
 {
 //lấy user, pwd, nếu không thấy giá trị mặc định là rỗng
 String user=pre.getString("user""");
 String pwd=pre.getString("pwd""");
 edituser.setText(user);
 editpassword.setText(pwd);
 }
 chksaveaccount.setChecked(bchk);
 }
 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
 // Inflate the menu; this adds items to the action bar if it is present.
 getMenuInflater().inflate(R.menu.activity_main, menu);
 return true;
 }
}

– Bạn xem source code DangNhapThanhCongActivity.java:

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
package tranduythanh.com;
 
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
 
public class DangNhapThanhCongActivity extends Activity {
 
TextView txtMsg;
 Button btnThoat;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_dang_nhap_thanh_cong);
 txtMsg=(TextView) findViewById(R.id.txtmsg);
 btnThoat=(Button) findViewById(R.id.btnThoat);
 btnThoat.setOnClickListener(new OnClickListener() {
 
 @Override
 public void onClick(View arg0) {
 // TODO Auto-generated method stub
 finish();
 }
 });
 
 Intent i=getIntent();
 txtMsg.setText("Hello : "+i.getStringExtra("user"));
 }
 
@Override
 public boolean onCreateOptionsMenu(Menu menu) {
 // Inflate the menu; this adds items to the action bar if it is present.
 getMenuInflater().inflate(R.menu.activity_dang_nhap_thanh_cong, menu);
 return true;
 }
 
}

– Như vậy là đã xong, bạn đã biết được cách lưu trạng thái. Chú ý là lưu trạng thái chỉ lưu Primitive Data, không cho phép lưu Object Serialize.

– Bạn click vào đây để tải coding mẫu: http://www.mediafire.com/download/4e4lyykdukgof82/LearnSharedPreferences.rar

————————————————————————————————————————–

-B- cách tạo Shared Preference Change Listeners để tạo giao diện cấu hình:

Bước 1:

– Tạo một Project Android bất kỳ, sau đó tạo thêm một layout PreferenceScreen:

30_SharedPreferences_6

– đặt tên tùy ý (ở đây Tôi đặt tên mypreferencelayout)

– Không giống như layout bình thường, preference screen sẽ nằm trong thư mục xml như hình bên dưới:

30_SharedPreferences_7

– Tiến hành tạo Checkbox giống như trên. (nó có nhiều control khác, tùy vào nhu cầu)

Bước 2:

– Tạo Activity cho Prefence layout trên (Kế thừa từ PreferenceActivity):

30_SharedPreferences_8

Bước 3:

– Cấu hình Manifest XML cho Preference Activity ở trên:

30_SharedPreferences_9

Bước 4:

– Tiến hành sửa MainActivity để sử dụng Preference Activity (Đăng ký OnSharePreferenceChangeListener):

30_SharedPreferences_10

– Bạn có thể xem kết quả như bên dưới:

30_SharedPreferences_11

————————————————————————————————————————–

Ví dụ 2:

– Bạn cố gắng tự xem lại lý thuyết và đọc coding để hiểu. Ví dụ này Tôi không giải thích coding.

– Hay bạn cần tạo ra các màn hình để cấu hình (giống như màn hình Setting của thiết bị chẳng hạn – cần biết Shared Preference Change Listeners):

30_SharedPreferences_1

*******

Một số tài liệu và khoá học bổ ích dành cho bạn: 

# Giáo trình: Lập Trình Android [Click để xem]

# Khoá học online:  Lập trình Android toàn tập [Click để xem]


# Nguồn duythanhcse

Tìm kiếm nội dung khác: