May 12, 2012

Danh sách liên kết (Linked List)

Ngộ nhận ra bài này mình viết code rất dở, để khi nào rảnh sẽ edit lại. Danh sách liên kết có những ứng dụng rất thú vị

Hôm nay Kỹ Thuật Lập Trình được học danh sách liên kết, vì ở dưới chăm chú sửa cái code thực hành mà không bắt kịp vấn đề, dù những định nghĩa căn bản đều hiểu. Cần phải tìm hiểu nó một cách cụ thể.

1.Khái niệm: Danh sách liên kết (linked list) là một cấu trúc dữ liệu bao gồm một nhóm các nút (nodes) tao thành một chuỗi.  Thông thường mỗi nút gồm dữ liệu (data) ở nút đó và tham chiếu (reference) đến nút kế tiếp trong chuỗi.
Danh sách liên kết là một trong những cấu trúc dữ liệu đơn giản và phổ biến nhất.
(Nguồn: Wikipedia)
clip_image001
Ưu điểm:
  • Cung cấp giải pháp để chứa cấu trúc dữ liệu tuyến tính.
  • Dễ dàng thêm hoặc xóa các phần tử trong danh sách mà không cần phải cấp phát hoặc tổ chức lại trật tự của mảng.
  • Cấp phát bộ nhớ động
Nhược điểm:
  • Một danh sách liên kết đơn giản không cho phép truy cập ngẫu nhiên dữ liệu.
  • Chính vì lí do trên mà một số phép tính như tìm phần tử cuối cùng, xóa phần tử ngẫu nhiên hay chèn thêm, tìm kiếm có thể phải duyệt tất cả các phần tử.
Phân loại:
  • Danh sách tuyến tính (Linear list):
clip_image002
  • Danh sách vòng (circular list):
clip_image003
  • Danh sách liên kết đôi (Double list):
clip_image004
Cấu trúc:
clip_image005
Data: Thành phần chứa một hay nhiều biến dữ liệu.
Next ptr: Tham chiếu trỏ đến phần tử kế tiếp trong cấu trúc.
Head: biến tham chiếu trỏ đến phần tử đầu tiên của danh sách.
clip_image006

Struct LLnode {
DataType Data;
LLnode* next;
};
 
2.Các phép toán:
Cho cấu trúc đơn giản:
struct LLintNode {
int Data;
struct LLintNode* Next;
};
Đếm số phần tử của Linked List:
Duyệt từng phần tử rồi đếm, cho đến khi nào gặp phần tử cuối.


int LengthLL(LLNode* head) {
int length = 0;
while (head != NULL) {
++length;
head = head ->Next;
}
return length;
}
Thêm một phần tử vào cuối linked list:
Nếu danh sách rỗng, thêm nút vào head.
Ngược lại, tìm phần tử cuối cùng của danh sách rồi thêm nút mới vào Next của nút cuối cùng đó:


void AddLast(LLNode** head, int data) {
LLNode** tmp = head;
 
LLNode* NewNode;
NewNode = (LLNode*) malloc(sizeof(LLNode));
NewNode->Data = data;
NewNode->Next = NULL;
 
if ((*tmp) == NULL) {
(*tmp) = NewNode;
} else {
while ((*tmp)->Next !=NULL) {
tmp = &((*tmp)->Next);
}
(*tmp)->Next = NewNode;
}
}
 
Xóa phần tử cuối cùng:
Tìm phần tử cuối cùng của danh sách, rồi gán bằng NULL.


void RemoveLast(LLNode** head) {
    LLNode** tmp = head;
    if ((*tmp) !=NULL) {
        while ((*tmp)->Next != NULL) {
            tmp = &((*tmp)->Next);
        }
    }
    (*tmp) = NULL;
 
}
Thêm phần tử vào đầu danh sách:
clip_image007




void AddFirst(LLNode** head, int Data) {
    LLNode** tmp = head;
    LLNode* NewNode;
    
    NewNode = (LLNode*) malloc(sizeof(LLNode));
    NewNode->Data = Data;
    NewNode->Next = (*head);
    
    (*tmp) = NewNode;
 
}
Xóa phần tử đầu tiên:
Nếu danh sách khác rỗng, đưa phần tử Next lên phía trước.


void RemoveFirst(LLNode** head) {
    LLNode** tmp = head;
    if ((*tmp) != NULL) {
        (*tmp) = (*tmp)->Next;
    }
}
Tìm kiếm phần tử trong danh sách:


LLNode* FindNode(LLNode** head,LLNode* src) {
    LLNode** tmp = head;
    while ( src->Data != (*tmp)->Data) {
        tmp = &((*tmp)->Next);
    }
    return (*tmp);
 
}
Chèn thêm 1 phần tử sau phần tử currrent:


int InsertNode(LLNode** head, LLNode* current, LLNode* NewNode) {
    LLNode** tmp = head;
    while ((current != (*tmp)) && (*tmp != NULL)) {
        tmp = &((*tmp)->Next);
    }
    
    if (*tmp == NULL) {
        return 0;
    } else {
        NewNode->Next= current->Next;
        current->Next = NewNode;
        
        (*tmp) = current;
        return 1;
    }
        
 
}
Xóa phần tử bất kì biết trước dữ liệu:

int RemoveNode(LLNode** head, LLNode* current) {
    LLNode** tmp = head;
    while ((current != (*tmp)) && (*tmp != NULL)) {
        tmp = &((*tmp)->Next);
    }
    if (*tmp == NULL) {
        return 0;
    } else {
        (*tmp) = (*tmp)->Next;
        return 1;
    }
}
3.Tham khảo:

May 3, 2012

Vẽ các hình học cơ bản trong OpenGL.


Tạo một dự án Empty Project và thiết lập cài đặt dành cho glut và OpenGL. Có thể tham khảo tại đây.
Gõ (hoặc chép) đoạn code sau:
#include <iostream>
#include <gl\glut.h>
#include <gl\GL.h>
#include <gl\GLU.h>
 
void display() {
 
    glColor3f(1.0,1.0,0.0);
 
    glPointSize(10);
    glBegin(GL_POINTS);
        glVertex2f(50,100);
        glVertex2f(200,200);
    glEnd();
    glFlush();
}
int main(int argc, char** argv) {
    glutInit(&argc,argv);
    
 
    glutInitDisplayMode(GLUT_RGB|GLUT_SINGLE);
    glutInitWindowPosition(0,0);
    glutInitWindowSize(500,500);
    glutCreateWindow("Ve");
    
    glClearColor(0.0,0.0,0.0,0.0);
    glClear(GL_COLOR_BUFFER_BIT);
 
    glOrtho(0.0,500,0.0,500,-1.0,1.0);
 
    glutDisplayFunc(display);
    glutMainLoop();
    return 0;
}

Phần #include: để nhập các thư viện, file header để sử dụng các hàm glut, OpenGL.

Nghiên cứu các hàm được gọi trong Main() trước:


  • glutInit(int* pargc,char **argv): Khởi tạo thông số cho cửa sổ của glut. Các tham số pargc và argv được lấy từ hàm main().
  • Các hàm về thiết lập cửa sổ màn hình:

    • glutInitDisplayMode(int mode): thiết lập chế độ màu, buffer cho màn hình.

Danh sách các thiết lập:

GLUT_RGBA: thiết lâp cho chế độ màn hình RGBA.

GLUT_RGB: tương tự GLUT_RGBA.

GLUT_INDEX: thiết lập chế độ màu mặc định.

GLUT_SINGLE: thiết lập chế độ màn hình buffer đơn.

GLUT_DOUBLE: thiết lập chế độ màn hình buffer đôi.

GLUT_ACCUM: thiết lập chế độ accumulation buffer.

GLUT_ALPHA: thiết lập chế độ đối tương anpha cho buffer màu.

GLUT_DEPTH: thiết lập cho depth buffer.

GLUT_STENCIL: thiết lập cho stencil buffer.

GLUT_MULTISAMPLE: thiết lập cho multisampling.

GLUT_STEREO: thiết lập chế độ stereo.

GLUT_LUMINANCE: thiết lập cho chế độ màu "luminance".

Sử dụng toán tử OR để kết hợp các chế độ màu, buffer với nhau.


  • glutInitWindowPosition(int x, int y): tọa độ cửa sổ màn hình.
  • glutInitWindowSize(int x, int y): thiết lập kích thước cửa sổ màn hình.
  • glutCreateWindow(char* title): tạo cửa sổ màn hình với tiêu đề là title.


  • glClearColor(Glclampf red, Glclampf green, Glclampf blue, Glclampf alpha): thiết lập chế độ màu mới cho toàn bộ ứng dụng.
  • glClear(int mode): GL_COLOR_BUFFER_BIT: thông số để thiết lập cho màu sắc. Còn một số thiết lập nữa sẽ được đề cập sau.
  • glOrtho(...): thiết lập tầm nhìn trực giao, sẽ được nói chi tiết trong bài về phép biến trong OpenGL.


  • glutDisplayFunc(void (*f)(void)): gọi hàm để thực hiện việc vẽ khi cửa sổ màn hình hiển thị. Trong ví dụ này là gọi hàm display.
  • glutMainLoop(): Lặp đi lặp lại các hàm callback - sẽ đề cập sau - đến khi nao cửa sổ đóng lại.

Trong hàm display:


  • glColor3f( red, green, blue ): thiết lập màu sắc cho đối tượng sắp vẽ.
  • glPointSize(float size): kích thước điểm ảnh.
  • glBegin(int mode): gồm các thông số và minh họa phía dưới:

clip_image001


  • glEnd():kết thúc quá trình vẽ.
  • glFlush(): đưa dữ liệu từ bộ nhớ tạm và màn hình.










May 2, 2012

Static: biến, hàm, các vấn đề xung quanh

Định nghĩa:

Trong khoa học máy tính, biến tĩnh (static variable) là biến được cấp phát tĩnh có thời gian sống (lifetime) xuyên suốt quá trình chạy chương trình. Các biến này chỉ được khởi tạo một lần, sau đó sẽ không mất đi.

Ví dụ:

#include <iostream>
void func() {
    static int a = 0;
    ++a;
    std::cout<< "A  = " << a << std::endl;
}
int main(int argc, char** argv) {
    func();
    func();
    func();
    //a = 7; // error.
    return 0;
}

Một điều lưu ý, tuy biến static có thời gian sống suốt cả chương trình, nhưng tầm nhìn (visibility)chỉ trong hàm, tức là chỉ có thể tính toán biến đó trong hàm.


static là một từ khóa trong C/C++ liên quan đến khái niệm lớp lưu trữ (storage class). Trong lớp lưu trữ còn có các đối từ khóa khác đó là : auto, register, extern.


Với chức năng liên quan đến khái niệm lớp lưu trữ, static còn có một số công dụng sau:



  • Static local variables: Như trên.


  • Static function: Khi có 2 hàm hoàn toàn giống nhau về tên, tham số, giá trị trả về, thì chương trình sẽ thực thi hàm chứa từ khóa static trong file đó.

Nói có vẻ khó hiểu như nếu code ví dụ thì sẽ rất rõ ràng:


ví dụ có hàm func1() trong cả 2 file.


file1.h và func1.cpp:



#ifndef _FUNC1_H
#define _FUNC1_H
void func1();
#endif


#include <iostream>
void func1() {
    std::cout << "Hello" << std::endl; 
}

Và func2.cpp



#include <iostream>
#include "func1.h"
//void func1() {
static void func1() {
    std::cout << "Goodbye" << std::endl;
}
int main(int argc, char** argv) {
    func1();
    return 0;
}

Output sẽ là "Goodbye".



  • Static in class: Khi khai báo static một thuộc tính hay phương thức của 1 class. Ta có thể sử dụng ngay mà không cần khai báo đối tượng.

Ví dụ:



#include <iostream>
class NUM {
public:
    static int a;
    int b;
    static void func() {
        std::cout<< "Hi!!" << std::endl;
    }
    void func1() {
        std::cout<< "Aloha!!" << std::endl;
    }
};
int NUM::a = 9;
int main(int argc, char** argv) {
    //NUM::b = 10; 
    //int NUM::a = 9;
    NUM::func();
    //NUM::func1();
    return 0;
}

 


Nguồn tham khảo:



  1. http://en.wikipedia.org/wiki/Static_variable .


  1. Cộng đồng C việt.

  2. Learncpp.com

  3. cprogramming.com