Bài giảng Cấu trúc dữ liệu: Ngăn xếp cung cấp cho người học những kiến thức như: Sử dụng mảng; Sử dụng con trỏ; Ứng dụng của ngăn xếp. Mời các bạn cùng tham khảo!
Trang 1TS Lê Minh Trung ThS Lương Trần Ngọc Khiết Khoa Công nghệ Thông tin- Đại học Sư phạm TP HCM
Trang 2Ngăn Xếp (Stack)
Sử dụng mảng
Sử dụng con trỏ
Ứng dụng của ngăn xếp
Trang 3Mô tả stack
Một stack là một cấu trúc
dữ liệu mà việc thêm vào và loại bỏ được thực hiện tại một đầu (gọi là đỉnh – top của stack).
Là một cấu trúc vào sau ra
trước – LIFO (Last In
First Out)
Trang 4Hoạt động của Stack
Stack rỗng:
Đẩy (push) Q vào:
Đẩy A vào:
Lấy (pop) ra một => được A:
Lấy ra một => được Q và stack rỗng:
Q
QA
QA
Q
Trang 5Hoạt động của Stack
Trang 6Thiết kế của Stack
template < class NodeType > //NodeType là kiểu dữ liệu tùy ý
class Stack
{
public :
Stack( void ); //phương thức khởi tạo
Stack( const Stack < NodeType > &source); //phương thức khởi tạo
~Stack( void ); //phương thức hủy
bool IsEmpty() const ;
void Push( const NodeType &item); //thêm phần tử vào đỉnh stack
void Pop(); //gỡ phần tử ra khỏi đỉnh stack
void Clear(); //xóa dữ liệu của stack
void operator=( const Stack < NodeType > &source);
};
Trang 8Cài đặt Stack sử dụng mảng
Trang 9const int MAX=20; //stack có tối đa MAX phần
bool IsEmpty() const;//kiểm tra stack rỗng
bool IsFull() const; //kiểm tra stack đầy
void Push(const NodeType &item);
NodeType &Peek() const;
void Pop();
void Clear();
private:
NodeType data[MAX]; //mảng chứa dữ liệu
int top; //đỉnh của stack
};
Thiết kế Stack dùng mảng
Trang 10Các phương thức
template<class NodeType>
Stack<NodeType>::Stack(void)
{
top =-1;
}
template<class NodeType>
void Stack<NodeType>::Clear()
{
top =-1;
}
template<class NodeType>
bool Stack<NodeType>::IsEmpty() const
{
return top==-1; //stack rỗng}
template<class NodeType>
bool Stack<NodeType>::IsFull() const
{
return top== MAX-1; //stack đầy}
Trang 11Các phương thức
template < class NodeType >
void Stack < NodeType >::Push( const NodeType & item ) {
Trang 12Các phương thức
template<class NodeType>
void Stack<NodeType>::Push(const NodeType &item){
template<class NodeType>
void Stack<NodeType>::Pop(){
Trang 14}catch(exception &e){
cout<< e.what(); cout<<endl;
}
}
Trang 16Cài đặt Stack sử dụng con trỏ
Trang 17Thiết kế Node
Node
Cần:
Dữ liệu Con trỏ để trỏ đến node sau Constructor
Trang 18Thiết kế Node
template < class NodeType >
struct Node
{
Node(); //phương thức khởi tạo
Node( NodeType item, Node < NodeType > *ptr= NULL ); //phương
thức khởi tạo };
Trang 19Thiết kế Node
#include "Node.h"
template < class NodeType >
Node < NodeType >::Node(){}
template < class NodeType >
Node < NodeType >::Node( NodeType item , Node < NodeType >
* ptr = nullptr ) {
this ->item= item ;
this ->next = ptr ;
}
Trang 20Ví dụ với Node liên kết
b
p1
c p2
Trang 21Stack liên kết
Trang 22Stack(void); //phương thức khởi tạo
Stack(const Stack < NodeType > &source); //phương thức khởi tạo
~Stack(void); //phương thức hủy
bool IsEmpty() const;
void Push(const NodeType &item); //thêm phần tử vào đỉnh stack
void Pop(); //gỡ phần tử ra khỏi đỉnh stack
NodeType &Peek() const; //xem phần tử trên đỉnh stack
void Clear(); //xóa dữ liệu của stack
void operator=(const Stack < NodeType > &source); //so sánh bằng
private:
Node < NodeType > *top;
Node < NodeType > *Copy(const Stack < NodeType > &source); //copy từ stack source tới
vùng dữ liệu mới và trả về con trỏ tới vùng này
};
Trang 23Thêm vào một stack liên kết
Giải thuật
1 Tạo ra một node mới với giá trị cần thêm vào
2 Trỏ nó đến đỉnh hiện tại của stack
3 Trỏ đỉnh của stack vào node mới
new_node new_top
top_node
Trang 24Phương thức Push
template < class NodeType >
void Stack < NodeType >::Push( const NodeType & item )
Trang 25Bỏ đỉnh của một stack liên kết
Giải thuật:
1 Gán một con trỏ để giữ đỉnh của stack
2 Trỏ đỉnh của stack vào node ngay sau đỉnh hiện tại
3 Xóa node cũ đi
old_top
top_node
old top middle old last
Trang 26Phương thức Pop
template < class NodeType >
void Stack < NodeType >::Pop(){
Node < NodeType > *oldTop = top; top = oldTop ->next;
delete oldTop;
}
Trang 27Sự không an toàn con trỏ trong C++
Kết thúc biến stack nhưng bộ nhớ còn lại:
delete stack0;
Gán hai stack: cả hai dùng chung một vùng dữ liệu
stack2 = stack1;
Trang 28Đảm bảo an toàn con trỏ trong C++
Destructor (phương thức hủy):
Sẽ được gọi ngay trước khi đối tượng kết thúc thời gian sống
Dùng xóa hết vùng dữ liệu
Copy constructor:
Sẽ được gọi khi khởi tạo biến lúc khai báo, hoặc truyền dữ liệu bằng tham trị
Sao chép nguồn thành một vùng dữ liệu mới
Assignment operator (toán tử gán):
Sẽ được gọi khi gán đối tượng này vào đối tượng khác
Xóa vùng dữ liệu của đích và đồng thời sao chép nguồn thành một vùng dữ liệu mới
Trang 29Hủy vùng nhớ đã được cấp cho Stack
template < class NodeType >
void Stack < NodeType >:: Clear()
{
while (!IsEmpty())Pop();
}
template < class NodeType >
Stack < NodeType >::~Stack( void )
{
Clear();
}
Trang 31Sao chép vùng dữ liệu
Giải thuật:
1 Tạo một đỉnh của danh sách mới với dữ liệu của đỉnh nguồn
2 Giữ một con trỏ đuôi chỉ vào cuối danh sách mới
2 Duyệt qua danh sách nguồn
2.1 Tạo một node mới với dữ liệu từ node nguồn hiện tại 2.2 Nối vào cuối danh sách mới
2.3 Con trỏ đuôi là node mới
Trang 32Sao chép vùng dữ liệu
template<class NodeType>
Node<NodeType> *Stack<NodeType>::Copy(const Stack<NodeType> &source){
Node<NodeType> *sTop, *sCur, *newTop, *newCur;
sTop = sCur = source.top;
if(sTop==nullptr)return nullptr;
newTop = newCur = new Node<NodeType>(sTop->item);
while (sCur->next !=nullptr)
{
sCur = sCur ->next;
newCur->next = new Node<NodeType>(sCur->item);
newCur = newCur ->next;
}
return newTop;
}
Trang 33Copy constructor và toán tử gán
template < class NodeType >
Stack < NodeType >::Stack( const Stack < NodeType > & source ) {
top = Copy( source );
}
template < class NodeType >
void Stack < NodeType >::operator=( const Stack < NodeType >
& source ) {
Clear(); //xóa dữ liệu cũ của stack
top = Copy( source ); //trỏ tới dữ liệu mới
}
Trang 34Phương thức khác
template < class NodeType >
bool Stack < NodeType >::IsEmpty() const
{
return (top== nullptr );
}
template < class NodeType >
NodeType & Stack < NodeType >::Peek() const
{
return top ->item;
}
Trang 35Stack<string> sString, sStr;
sString.Push("you !"); sString.Push("love"); sString.Push("I");sStr = sString;
Trang 36} catch ( exception &e){
cout<< e.what()<< endl;
}
cin.get();
}
Trang 39Ước lượng biểu thức
Cho trước biểu thức: (2+4)*5 – (3-2)*4
Biểu thức trung tố (infix)
1 Có thể biểu diễn biểu thức ở dạng khác mà không
cần dấu ( ) hay không?
o 2 4 + 5 * 3 2 – 4 *
-o Biểu thức tiền tố (postfix)
2 Lượng giá biểu thức như thế nào?
o 2 4 + 5 * 3 2 – 4 * 6 5 * 3 2 – 4 * 30 3 2 – 4 *
- 30 1 4 * - - 30 4 - - 26
Trang 40Lượng giá biểu thức postfix
Đọc lần lượt các thành phần của biểu thức từ trái sang phải, với
mỗi thành phần được đọc thực hiện các bước sau:
Nếu thành phần được đọc là toán hạng od thì đẩy nó vào ngăn xếp, tức là S Push (od).
Nếu thành phần được đọc là phép toán op thì lấy ra các toán
hạng ở đỉnh ngăn xếp:
od 2 = S.PopAndPeek( )
od 1 = S.PopAndPeek( )
Thực hiện phép toán op với các toán hạng là od 1 và od 2, kết
quả được đẩy vào ngăn xếp:
r = od 1 op od 2
S Push(r)
Lặp lại hai bước trên cho tới khi thành phần cuối cùng của
biểu thức được đọc qua Khi đó ngăn xếp chứa kết quả của biểu thức.
Trang 411 3
2 8 5
4 54 5
3 5 12
17
Trang 42Chuyển đổi infix thành postfix
Thứ tự ưu tiên của toán tử: (), * / , +
-1. Nếu thành phần được đọc là toán hạng thì viết nó vào biểu thức postfix.
2. Nếu thành phần được đọc là phép toán (phép toán hiện thời), thì thực hiện các
bước sau:
1. Nếu ngăn xếp không rỗng thì nếu phần tử ở đỉnh ngăn xếp là phép toán có
quyền ưu tiên cao hơn hay bằng phép toán hiện thời, thì phép toán đó
được kéo ra khỏi ngăn xếp và viết vào biểu thức postfix Lặp lại bước này
2. Nếu ngăn xếp rỗng hoặc phần tử ở đỉnh ngăn xếp là dấu mở ngoặc hoặc
phép toán ở đỉnh ngăn xếp có quyền ưu tiên thấp hơn phép toán hiện thời, thì phép toán hiện thời được đẩy vào ngăn xếp.
3. Nếu thành phần được đọc là dấu mở ngoặc thì nó được đẩy vào ngăn xếp.
4. Nếu thành phần được đọc là dấu đóng ngoặc thì thực hiện các bước sau:
1. (Bước lặp) Loại các phép toán ở đỉnh ngăn xếp và viết vào biểu thức postfixcho tới khi đỉnh ngăn xếp là dấu mở ngoặc
2. Loại dấu mở ngoặc khỏi ngăn xếp
5. Sau khi toàn bộ biểu thức infix được đọc, loại các phép toán ở đỉnh ngăn xếp vàviết vào biểu thức postfix cho tới khi ngăn xếp rỗng
Trang 43Ví dụ
Input: a * ( b – c + d) + e / f
a b c – d + * e f / +
Trang 44CÁM ƠN VÌ ĐÃ LẮNG NGHE!