Bài tập lớn: Các thao tác trên danh sách liên kết (Cấu trúc dữ liệu & Thuật toán)

Bài tập lớn môn cấu trúc dữ liệu và thuật toán: Tổng hợp đề tài, hướng dẫn chi tiết, giúp sinh viên hoàn thành bài tập hiệu quả, đạt điểm cao.

Trường đại học

Đại Học Phenikaa

Người đăng

Ẩn danh

Thể loại

Bài tập lớn kết thúc học phần
43
1
0

Phí lưu trữ

30 Point

Mục lục chi tiết

1. Các thao tác trên danh sách liên kết

1.1. Tạo, cấp phát, và giải phóng bộ nhớ cho 1 nút listnode p

1.2. Chèn một nút vào đầu danh sách

1.3. Chèn một nút vào cuối danh sách

1.4. Chèn một nút vào trước nút r trong danh sách

1.5. Xóa một nút ở đầu danh sách

1.6. Xóa một nút ở cuối danh sách

1.7. Xóa một nút trước nút r danh sách

1.8. Duyệt toàn bộ danh sách

2. Chương trình chạy:

3. Các thao tác trên hàng đợi

3.1. Thao tác khởi tạo hàng đợi

3.2. Thao tác kiểm tra hàng đợi rỗng

3.3. Thao tác thêm 1 phần tử vào hàng đợi

3.4. Lấy phần tử ra khỏi hàng đợi

3.5. Cài đặt hàng đợi bằng danh sách liên kết

4. Các thao tác trên ngăn xếp

4.1. Thao tác khởi tạo ngăn xếp

4.2. Thao tác kiểm tra ngăn xếp rỗng

4.3. Thao tác kiểm tra ngăn xếp đầy

4.4. Thao tác bổ sung 1 phần tử vào ngăn xếp

4.5. Thao tác lấy 1 phần tử ra khỏi ngăn xếp

5. Duyệt cây nhị phân

5.1. Duyệt thứ tự trước

5.2. Duyệt thứ tự giữa

5.3. Duyệt thứ tự sau

6. Tìm kiếm và chèn trên cây tìm kiếm nhị phân

6.1. Tìm kiếm khóa

6.2. Chèn một nút vào BST

7. Giải thuật sắp xếp cơ bản

7.1. Sắp xếp chọn

Tóm tắt

I. Danh Sách Liên Kết Tổng Quan Lợi Ích và Hạn Chế Cơ Bản

Danh sách liên kết, hay linked list, là một cấu trúc dữ liệu tuyến tính, trong đó các phần tử không được lưu trữ liên tục trong bộ nhớ. Mỗi phần tử, gọi là node, chứa dữ liệu và một hoặc nhiều con trỏ (pointer) đến node tiếp theo trong chuỗi. Điều này khác biệt so với mảng (array), nơi các phần tử nằm liền kề nhau trong bộ nhớ. Danh sách liên kết mang lại sự linh hoạt trong việc chèn và xóa các phần tử mà không cần phải di chuyển các phần tử khác, điều này có thể tốn kém về mặt hiệu năng đối với mảng. Tuy nhiên, việc truy cập ngẫu nhiên vào một phần tử trong danh sách liên kết đòi hỏi phải duyệt từ đầu (head) danh sách, dẫn đến thời gian truy cập chậm hơn so với mảng. Dynamic memory allocation là một yếu tố quan trọng trong việc triển khai danh sách liên kết, vì nó cho phép danh sách phát triển và thu hẹp khi cần thiết. Danh sách liên kết có nhiều biến thể, bao gồm singly linked list, doubly linked list, và circular linked list, mỗi loại có những ưu điểm và nhược điểm riêng phù hợp với các ứng dụng khác nhau. Việc lựa chọn giữa danh sách liên kết và mảng phụ thuộc vào yêu cầu cụ thể của ứng dụng, đặc biệt là liên quan đến tần suất chèn/xóa, truy cập ngẫu nhiên và sử dụng bộ nhớ. Theo tài liệu gốc, "Danh sách liên kết có thể được xem như là 1 sự bố trí tuần tự các phần tử trong 1 tập. Bắt đầu từ 1 nút, ta coi đó là phần tử đầu tiên trong danh sách". Điều này nhấn mạnh bản chất tuần tự của cấu trúc dữ liệu này.

1.1. So Sánh Danh Sách Liên Kết và Mảng Ưu Nhược Điểm

Sự khác biệt lớn nhất giữa array vs linked list nằm ở cách chúng lưu trữ dữ liệu và truy cập các phần tử. Mảng lưu trữ các phần tử liên tiếp trong bộ nhớ, cho phép truy cập ngẫu nhiên bằng chỉ số (index) với thời gian O(1). Tuy nhiên, việc chèn hoặc xóa một phần tử ở giữa mảng đòi hỏi phải di chuyển các phần tử khác, dẫn đến thời gian O(n). Ngược lại, danh sách liên kết lưu trữ các phần tử phân tán trong bộ nhớ, và mỗi phần tử (node) chứa một con trỏ đến phần tử tiếp theo. Việc chèn hoặc xóa một node trong danh sách liên kết chỉ đòi hỏi cập nhật các con trỏ, với thời gian O(1) nếu có con trỏ đến node cần thao tác. Tuy nhiên, việc truy cập một phần tử ở vị trí bất kỳ trong danh sách liên kết đòi hỏi phải duyệt từ đầu, với thời gian O(n). Về mặt sử dụng bộ nhớ, mảng yêu cầu cấp phát một khối bộ nhớ liên tục, trong khi danh sách liên kết có thể cấp phát bộ nhớ cho từng node riêng lẻ. Điều này có thể dẫn đến việc sử dụng bộ nhớ hiệu quả hơn của danh sách liên kết trong một số trường hợp. Do đó, lựa chọn giữa mảng và danh sách liên kết phụ thuộc vào các yếu tố như tần suất chèn/xóa, truy cập ngẫu nhiên và quản lý bộ nhớ.

1.2. Các Loại Danh Sách Liên Kết Đơn Đôi và Vòng tròn

Có ba loại chính của danh sách liên kết: singly linked list, doubly linked list, và circular linked list. Trong singly linked list, mỗi node chỉ chứa một con trỏ đến node tiếp theo. Điều này làm cho việc duyệt danh sách trở nên đơn giản, nhưng việc duyệt ngược lại thì không thể thực hiện được một cách trực tiếp. Doubly linked list khắc phục hạn chế này bằng cách mỗi node chứa hai con trỏ: một con trỏ đến node tiếp theo và một con trỏ đến node trước đó. Điều này cho phép duyệt danh sách theo cả hai chiều. Tuy nhiên, doubly linked list đòi hỏi nhiều bộ nhớ hơn để lưu trữ thêm con trỏ. Trong circular linked list, node cuối cùng trỏ đến node đầu tiên, tạo thành một vòng tròn. Điều này có thể hữu ích trong các ứng dụng mà việc duyệt danh sách lặp đi lặp lại là cần thiết, chẳng hạn như trong việc quản lý tài nguyên hệ thống. Mỗi loại danh sách liên kết có những ưu điểm và nhược điểm riêng, và việc lựa chọn loại nào phù hợp nhất phụ thuộc vào yêu cầu cụ thể của ứng dụng. Ví dụ, một ứng dụng cần duyệt danh sách theo cả hai chiều có thể hưởng lợi từ doubly linked list, trong khi một ứng dụng chỉ cần duyệt danh sách theo một chiều và quan tâm đến việc tiết kiệm bộ nhớ có thể sử dụng singly linked list.

II. Thách Thức và Vấn Đề Thường Gặp Với Danh Sách Liên Kết

Mặc dù danh sách liên kết cung cấp sự linh hoạt và hiệu quả trong nhiều tình huống, chúng cũng đi kèm với những thách thức và vấn đề tiềm ẩn. Một trong những vấn đề chính là việc quản lý bộ nhớ thủ công. Trong các ngôn ngữ như C và C++, việc cấp phát và giải phóng bộ nhớ cho các node trong danh sách liên kết là trách nhiệm của lập trình viên. Việc không giải phóng bộ nhớ khi các node không còn cần thiết có thể dẫn đến rò rỉ bộ nhớ, làm giảm hiệu năng của hệ thống. Một thách thức khác là việc duyệt danh sách liên kết. Để truy cập một node cụ thể, cần phải duyệt từ đầu danh sách, điều này có thể tốn kém về mặt thời gian đối với các danh sách lớn. Ngoài ra, việc thực hiện các thao tác như chèn hoặc xóa node ở giữa danh sách có thể trở nên phức tạp, đặc biệt là trong trường hợp của singly linked list, vì cần phải tìm node trước node cần thao tác. Các vấn đề khác có thể bao gồm việc xử lý các trường hợp đặc biệt, chẳng hạn như danh sách rỗng hoặc danh sách chỉ có một node, và việc đảm bảo tính đúng đắn của các con trỏ. Theo tài liệu gốc, việc "Tạo, cấp phát, và giải phóng bộ nhớ cho 1 nút" là một trong những thao tác cơ bản, nhấn mạnh tầm quan trọng của việc quản lý bộ nhớ.

2.1. Quản Lý Bộ Nhớ trong Danh Sách Liên Kết Rò Rỉ và Phân Mảnh

Memory management linked list là một khía cạnh quan trọng trong việc triển khai danh sách liên kết. Việc cấp phát bộ nhớ động cho từng node cho phép danh sách phát triển và thu hẹp khi cần thiết. Tuy nhiên, nếu bộ nhớ không được giải phóng đúng cách khi các node không còn được sử dụng, nó có thể dẫn đến rò rỉ bộ nhớ (memory leak). Rò rỉ bộ nhớ xảy ra khi bộ nhớ được cấp phát nhưng không bao giờ được giải phóng, dẫn đến việc giảm dần lượng bộ nhớ khả dụng cho hệ thống. Ngoài ra, việc cấp phát và giải phóng bộ nhớ liên tục có thể dẫn đến phân mảnh bộ nhớ (memory fragmentation), trong đó bộ nhớ khả dụng bị chia thành các khối nhỏ không liền kề, gây khó khăn cho việc cấp phát các khối bộ nhớ lớn hơn sau này. Để tránh các vấn đề này, cần phải đảm bảo rằng bộ nhớ được giải phóng đúng cách khi các node không còn cần thiết. Trong các ngôn ngữ như Java và Python, garbage collection tự động giúp giải phóng bộ nhớ không còn được sử dụng, giảm bớt gánh nặng cho lập trình viên. Tuy nhiên, trong các ngôn ngữ như C và C++, lập trình viên phải tự quản lý bộ nhớ một cách cẩn thận.

2.2. Độ Phức Tạp Thời Gian và Không Gian của Các Thao Tác

Hiểu được time complexity linked listspace complexity linked list là rất quan trọng để đánh giá hiệu quả của danh sách liên kết. Việc chèn và xóa node ở đầu danh sách có độ phức tạp thời gian là O(1), vì chỉ cần cập nhật các con trỏ. Tuy nhiên, việc chèn và xóa node ở cuối danh sách, hoặc ở một vị trí cụ thể, đòi hỏi phải duyệt từ đầu danh sách, dẫn đến độ phức tạp thời gian là O(n). Việc tìm kiếm một node trong danh sách liên kết cũng có độ phức tạp thời gian là O(n), vì cần phải duyệt từ đầu danh sách cho đến khi tìm thấy node cần tìm. Về mặt độ phức tạp không gian, danh sách liên kết có độ phức tạp là O(n), vì nó cần lưu trữ n node, mỗi node chứa dữ liệu và một hoặc nhiều con trỏ. Advantages of linked list so với mảng là khả năng chèn và xóa các phần tử một cách hiệu quả, đặc biệt là ở giữa danh sách. Disadvantages of linked list bao gồm việc truy cập ngẫu nhiên chậm hơn và yêu cầu bộ nhớ bổ sung để lưu trữ các con trỏ.

III. Các Thao Tác Cơ Bản Chèn Xóa và Tìm Kiếm Nút Hiệu Quả

Các thao tác cơ bản trên danh sách liên kết bao gồm chèn một nút (insert node linked list), xóa một nút (delete node linked list), tìm kiếm một nút (search node linked list) và duyệt danh sách (traversal linked list). Việc chèn một nút có thể được thực hiện ở đầu, cuối hoặc ở một vị trí cụ thể trong danh sách. Việc xóa một nút cũng tương tự, có thể được thực hiện ở đầu, cuối hoặc ở một vị trí cụ thể. Việc tìm kiếm một nút đòi hỏi phải duyệt từ đầu danh sách cho đến khi tìm thấy node cần tìm. Các thao tác này có thể được thực hiện một cách hiệu quả bằng cách sử dụng các con trỏ để cập nhật các liên kết giữa các node. Theo tài liệu gốc, có các hàm mẫu cho "Chèn một nút vào đầu danh sách", "Chèn một nút vào cuối danh sách" và "Xóa một nút ở đầu danh sách", cho thấy tầm quan trọng của các thao tác này.

3.1. Hướng Dẫn Chi Tiết Chèn Nút Đầu Cuối và Giữa Danh Sách

Để insert node linked list một cách hiệu quả, cần xem xét vị trí chèn. Chèn vào đầu danh sách chỉ đòi hỏi cập nhật con trỏ đầu (head) của danh sách và con trỏ của node mới, với thời gian O(1). Chèn vào cuối danh sách đòi hỏi duyệt từ đầu danh sách đến node cuối cùng, sau đó cập nhật con trỏ của node cuối cùng để trỏ đến node mới, với thời gian O(n). Chèn vào giữa danh sách đòi hỏi duyệt từ đầu danh sách đến vị trí cần chèn, sau đó cập nhật con trỏ của node trước và node mới, với thời gian O(n). Tuy nhiên, nếu có con trỏ đến node trước vị trí cần chèn, thì thời gian chèn có thể giảm xuống O(1). Điều quan trọng là phải xử lý các trường hợp đặc biệt, chẳng hạn như danh sách rỗng hoặc vị trí chèn không hợp lệ. Ví dụ, nếu muốn chèn vào vị trí thứ 5 trong một danh sách chỉ có 3 node, cần phải xử lý trường hợp này để tránh lỗi.

3.2. Phương Pháp Xóa Nút Loại Bỏ Phần Tử Khỏi Danh Sách

Việc delete node linked list cũng tương tự như việc chèn node, cần xem xét vị trí xóa. Xóa node ở đầu danh sách chỉ đòi hỏi cập nhật con trỏ đầu (head) của danh sách, với thời gian O(1). Xóa node ở cuối danh sách đòi hỏi duyệt từ đầu danh sách đến node gần cuối, sau đó cập nhật con trỏ của node gần cuối để trỏ đến NULL, với thời gian O(n). Xóa node ở giữa danh sách đòi hỏi duyệt từ đầu danh sách đến node trước node cần xóa, sau đó cập nhật con trỏ của node trước để trỏ đến node sau node cần xóa, với thời gian O(n). Tuy nhiên, nếu có con trỏ đến node trước node cần xóa, thì thời gian xóa có thể giảm xuống O(1). Điều quan trọng là phải giải phóng bộ nhớ của node đã xóa để tránh rò rỉ bộ nhớ. Tương tự như việc chèn node, cần phải xử lý các trường hợp đặc biệt, chẳng hạn như danh sách rỗng hoặc node cần xóa không tồn tại trong danh sách.

IV. Ứng Dụng Thực Tế Của Danh Sách Liên Kết Trong Lập Trình

Applications of linked list rất đa dạng và phổ biến trong nhiều lĩnh vực của khoa học máy tính. Chúng được sử dụng để triển khai các cấu trúc dữ liệu khác như ngăn xếp (stack) và hàng đợi (queue). Danh sách liên kết cũng được sử dụng trong việc quản lý bộ nhớ, biểu diễn đa thức, và trong các thuật toán đồ thị. Trong hệ điều hành, danh sách liên kết được sử dụng để quản lý các tiến trình và các tài nguyên hệ thống. Trong trình duyệt web, danh sách liên kết có thể được sử dụng để lưu trữ lịch sử duyệt web. Ngoài ra, danh sách liên kết còn được sử dụng trong các trình soạn thảo văn bản để quản lý các dòng văn bản. Theo tài liệu gốc, danh sách liên kết được sử dụng để "Cài đặt hàng đợi bằng danh sách liên kết", cho thấy một ứng dụng cụ thể.

4.1. Danh Sách Liên Kết trong Ngăn Xếp và Hàng Đợi

Danh sách liên kết có thể được sử dụng để triển khai ngăn xếp và hàng đợi một cách hiệu quả. Trong ngăn xếp, việc chèn và xóa phần tử (push và pop) luôn được thực hiện ở đầu danh sách, do đó thời gian thực hiện là O(1). Trong hàng đợi, việc chèn phần tử (enqueue) được thực hiện ở cuối danh sách và việc xóa phần tử (dequeue) được thực hiện ở đầu danh sách. Nếu sử dụng danh sách liên kết đơn, việc chèn phần tử vào cuối hàng đợi có thể tốn kém (O(n)), nhưng nếu sử dụng danh sách liên kết đôi hoặc lưu trữ con trỏ đến node cuối cùng, thì thời gian chèn có thể giảm xuống O(1). Việc sử dụng danh sách liên kết để triển khai ngăn xếp và hàng đợi cho phép các cấu trúc dữ liệu này phát triển và thu hẹp một cách linh hoạt, mà không bị giới hạn bởi kích thước cố định như khi sử dụng mảng.

4.2. Quản Lý Bộ Nhớ và Các Ứng Dụng Khác của Danh Sách Liên Kết

Ngoài việc triển khai ngăn xếp và hàng đợi, danh sách liên kết còn được sử dụng trong nhiều ứng dụng khác. Trong quản lý bộ nhớ, danh sách liên kết có thể được sử dụng để theo dõi các khối bộ nhớ đã được cấp phát và các khối bộ nhớ còn trống. Trong biểu diễn đa thức, mỗi node trong danh sách liên kết có thể đại diện cho một số hạng của đa thức, với dữ liệu là hệ số và số mũ. Trong các thuật toán đồ thị, danh sách liên kết có thể được sử dụng để biểu diễn danh sách kề của một đỉnh. Ví dụ, mỗi đỉnh trong đồ thị có thể có một danh sách liên kết chứa tất cả các đỉnh kề với nó. Danh sách liên kết cũng được sử dụng trong các trình soạn thảo văn bản để quản lý các dòng văn bản, cho phép chèn và xóa dòng một cách dễ dàng. Trong hệ điều hành, danh sách liên kết có thể được sử dụng để quản lý các tiến trình, các tệp tin, và các tài nguyên hệ thống khác.

V. Triển Khai Danh Sách Liên Kết trong Java C và Python

Danh sách liên kết có thể được triển khai trong nhiều ngôn ngữ lập trình khác nhau, bao gồm Java linked list, C++ linked list, và Python linked list. Cách triển khai có thể khác nhau tùy thuộc vào cú pháp và tính năng của từng ngôn ngữ, nhưng các khái niệm cơ bản vẫn giống nhau. Trong Java, có lớp LinkedList được xây dựng sẵn, cung cấp các phương thức để chèn, xóa và tìm kiếm các phần tử. Trong C++, có thể sử dụng con trỏ và các lớp để triển khai danh sách liên kết. Trong Python, có thể sử dụng các lớp và đối tượng để triển khai danh sách liên kết. Điều quan trọng là phải hiểu cách quản lý bộ nhớ trong từng ngôn ngữ để tránh các vấn đề như rò rỉ bộ nhớ. Theo tài liệu gốc, có các đoạn mã C mẫu cho các thao tác trên danh sách liên kết.

5.1. Ví Dụ Triển Khai Danh Sách Liên Kết Đơn trong Java

Trong Java, có thể triển khai danh sách liên kết đơn bằng cách tạo một lớp Node để đại diện cho một node trong danh sách, và một lớp LinkedList để đại diện cho toàn bộ danh sách. Lớp Node chứa dữ liệu và một con trỏ đến node tiếp theo. Lớp LinkedList chứa một con trỏ đến node đầu tiên (head) của danh sách và các phương thức để chèn, xóa và tìm kiếm các phần tử. Có thể sử dụng lớp LinkedList sẵn có của Java, hoặc có thể tự triển khai để hiểu rõ hơn về cách hoạt động của danh sách liên kết. Việc sử dụng lớp LinkedList sẵn có giúp tiết kiệm thời gian và công sức, nhưng việc tự triển khai giúp hiểu rõ hơn về các khái niệm cơ bản.

5.2. Triển Khai Danh Sách Liên Kết trong C Sử Dụng Con Trỏ và Lớp

Trong C++, có thể triển khai danh sách liên kết bằng cách sử dụng con trỏ và các lớp. Tương tự như trong Java, có thể tạo một lớp Node để đại diện cho một node trong danh sách, và một lớp LinkedList để đại diện cho toàn bộ danh sách. Tuy nhiên, trong C++, cần phải quản lý bộ nhớ một cách thủ công bằng cách sử dụng các toán tử newdelete. Việc không giải phóng bộ nhớ khi các node không còn cần thiết có thể dẫn đến rò rỉ bộ nhớ. Do đó, cần phải cẩn thận khi triển khai danh sách liên kết trong C++.

VI. Các Thuật Toán và Bài Toán Liên Quan Đến Danh Sách Liên Kết

Có nhiều linked list algorithms và bài toán liên quan đến danh sách liên kết. Một số thuật toán phổ biến bao gồm đảo ngược danh sách, tìm nút giữa, phát hiện vòng lặp, và hợp nhất hai danh sách đã được sắp xếp. Các bài toán liên quan đến danh sách liên kết thường yêu cầu sử dụng các con trỏ một cách khéo léo để giải quyết vấn đề. Việc nắm vững các khái niệm cơ bản về danh sách liên kết và các thao tác trên chúng là rất quan trọng để giải quyết các bài toán này.

6.1. Đảo Ngược Danh Sách Liên Kết Phương Pháp và Ứng Dụng

Đảo ngược danh sách liên kết là một bài toán phổ biến. Có hai phương pháp chính để đảo ngược danh sách liên kết: lặp (iterative) và đệ quy (recursive). Phương pháp lặp sử dụng ba con trỏ để duyệt qua danh sách và đảo ngược các liên kết giữa các node. Phương pháp đệ quy chia bài toán thành các bài toán con nhỏ hơn và đảo ngược danh sách một cách đệ quy. Ứng dụng của việc đảo ngược danh sách liên kết bao gồm việc hiển thị danh sách theo thứ tự ngược lại và việc giải quyết các bài toán liên quan đến danh sách liên kết.

6.2. Phát Hiện Vòng Lặp trong Danh Sách Liên Kết Thuật Toán Rùa và Thỏ

Phát hiện vòng lặp trong danh sách liên kết là một bài toán quan trọng. Thuật toán rùa và thỏ (Floyd's cycle-finding algorithm) là một phương pháp hiệu quả để phát hiện vòng lặp. Thuật toán này sử dụng hai con trỏ: một con trỏ di chuyển chậm (rùa) và một con trỏ di chuyển nhanh (thỏ). Nếu danh sách có vòng lặp, hai con trỏ sẽ gặp nhau. Nếu danh sách không có vòng lặp, con trỏ nhanh sẽ đạt đến cuối danh sách. Thuật toán này có độ phức tạp thời gian là O(n) và độ phức tạp không gian là O(1).

20/09/2025

Trích đoạn nội dung tài liệu

lOMoARcPSD|39108650 TRƯỜNG ĐẠI HỌC PHENIKAA KHOA KHOA HỌC CƠ BẢN ⸎⸎⸎⸎⸎ BÀI TẬP LỚN KẾT THÚC HỌC PHẦN Môn Cấu trúc dữ liệu và thuật toán Lớp Cấu trúc dữ liệu và thuật toán_1.2_LT Sinh viên : Đỗ Minh Quân Lớp : K14-CNTT3 Mã SV : 20010879 Downloaded by ANH LE (bachvan14@gmail. Các thao tác trên danh sách liên kết Danh sách liên kết là 1 cấu trúc dữ liệu có kiểu tuần tự, mỗi phần tử trong danh sách liên kết có chứa thông tin, qua đó ta có thể truy cập tới phần tử này. Danh sách liên kết có thể được xem như là 1 sự bố trí tuần tự các phần tử trong 1 tập. Bắt đầu từ 1 nút, ta coi đó là phần tử đầu tiên trong danh sách.

Từ nút này, theo liên kết mà nó trỏ tới, ta có nút thứ 2, được coi là phần tử thứ 2 trong danh sách, v. cứ tiếp tục như vậy cho đến hết danh sách. Nút cuối cùng có thể có liên kết là một liên kết null, tức là không trỏ tới nút nào, hoặc nó có thể trỏ về nút đầu tiên để tạo thành 1 vòng. • Các thao tác trên danh sách liên kết - Tạo, cấp phát, và giải phóng bộ nhớ cho 1 nút listnode p; // Khai báo biến p p = (listnode)malloc(sizeof(struct node));//cấp phát bộ nhớ cho p free(p); //giải phóng bộ nhớ đã cấp phát cho nút p; Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 - Chèn một nút vào đầu danh sách B1 Tạo và cấp phát bộ nhớ cho một nút mới ,đc trỏ tới bởi q B2 Sau khi gán các giá trị thích hợp cho phần tử của nút mới, cho con trỏ tiếp của nút mới trỏ đến phần tử đầu tiên của nút.

B3 Để p trỏ đến đầu nút danh sách ,ta cần cho p trỏ đến nút mới tạo void Insert_Begin(listnode *p, int x){ listnode q; q = (listnode)malloc(sizeof(struct node)); q-> item = x; q-> next = *p; *p = q; } Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 - Chèn một nút vào cuối danh sách B1 Dịch chuyển con trỏ tới nút cuối của danh sách. Để làm được việc này, ta phải khai báo một biến con trỏ mới r. Ban đầu, biến này, cùng với p, trỏ đến đầu danh sách. Lần lượt dịch chuyển r theo các nút kế tiếp cho tới khi đến cuối danh sách.

B2 Cho con trỏ tiếp của nút cuối (được trỏ tới bởi r) trỏ đến nút mới tạo là q, và con trỏ tiếp của q trỏ tới null void Insert_End(listnode *p, int x){ listnode q, r; q = (listnode)malloc(sizeof(struct node)); q-> item = x; q->next = NULL; if (*p==NULL) *p = q; else{ r = *p; while (r->next != NULL) r = r->next; r->next = q; Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 } } - Chèn một nút vào trước nút r trong danh sách B1 Cho con trỏ tiếp của nút mới trỏ đến nút kế tiếp của nút r. B2 Cho con trỏ tiếp của nút r trỏ đến q void Insert_Middle(listnode *p, int position, int x){ int count=1, found=0; listnode q, r; r = *p; while ((r != NULL)&&(found==0)){ if (count == position){ q = (listnode)malloc(sizeof(struct node)); q-> item = x; q-> next = r-> next; Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 r-> next = q; found = 1; } count ++; r = r-> next; } if (found==0) printf(“Khong tim thay vi tri can chen !”); } - Xóa một nút ở đầu danh sách B1 Dùng một con trỏ tạm q trỏ đến đầu danh sách B2 Dịch chuyển con trỏ p qua phần tử đầu tiên đến phần tử kế tiếp Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 B3 Ngắt liên kết của biến tạm q với nút tiếp theo, giải phóng bộ nhớ Cho q. void Remove_Begin(listnode *p){ listnode q; if (*p == NULL) return; q = *p; *p = (*p)-> next; q-> next = NULL; free(q); } Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 - Xóa một nút ở cuối danh sách B1 Dịch chuyển con trỏ tới nút gần cuối của danh sách. Để làm được việc này, ta phải dùng 2 biến tạm là q và r.

Lần lượt dịch chuyển q và r từ đầu danh sách tới cuối danh sách, trong đó q luôn dịch chuyển sau r một nút. Khi r tới nút cuối cùng thì q là nút gần cuối cùng nhất B2 Cho con trỏ tiếp của nút gần cuối cùng nhất (đang được trỏ bởi q) trỏ tới null. Giải phóng bộ nhớ cho nút cuối cùng (đang được trỏ bởi r) void Remove_End(listnode *p){ listnode q, r; if (*p == NULL) return; if ((*p)-> next == NULL){ Remove_Begin(*p); return; Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 } r = *p; while (r-> next != NULL){ q = r; r = r-> next; } q-> next = NULL; free(r); } - Xóa một nút trước nút r danh sách B1 Sử dụng một biến tạm q trỏ đến nút đứng trước nút r B2 Cho con trỏ của nút r trỏ tới nút đứng sau nút q. Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 B3 Ngắt liên kết của nút q và giải phóng bộ nhớ cho q void Remove_Middle(listnode *p, int position){ int count=1, found=0; listnode q, r; r = *p; while ((r != NULL)&&(found==0)){ if (count == position){ q = r-> next; r-> next = q-> next; q-> next = NULL; free (q); found = 1; } count ++; r = r-> next; Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 } if (found==0) printf(“Khong tim thay vi tri can xoa !”); } - Duyệt toàn bộ danh sách Thao tác duyệt danh sách cho phép duyệt qua toàn bộ các phần tử của danh sách, từ phần tử đầu tiền cho tới phần tử cuối cùng.

Để thực hiện thao tác này, ta cần một biến tạm r trỏ tới đầu danh sách. Từ vị trí này, theo liên kết của các nút, thực hiện duyệt qua từng phần tử trong danh sách. Trong quá trình duyệt, tại mỗi nút ta có thể thực hiện các thao tác cần thiết như lấy thông tin phần tử, sửa thông tin, so sánh, v. r = p; while (r-> next != null){ //thực hiện các thao tác cần thiết r = r-> next; } Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 Chương trình chạy: #include <stdio.h> struct node { int data; int key; struct node *next; }; Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 struct node *head = NULL; struct node *current = NULL; //hien thi danh sach void printList() { struct node *ptr = head; printf("\n[ "); //bat dau tu phan dau danh sach while(ptr != NULL) { printf("(%d,%d) ",ptr->key,ptr->data); ptr = ptr->next; } printf(" ]"); Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 } //chen link tai vi tri dau tien void insertFirst(int key, int data) { //tao mot link struct node *link = (struct node*) malloc(sizeof(struct node)); link->key = key; link->data = data; //tro link nay toi first node cu link->next = head; //tro first toi first node moi head = link; } Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 //xoa phan tu dau tien struct node* deleteFirst() { //luu tham chieu toi first link struct node *tempLink = head; //danh dau next toi first link la first head = head->next; //tra ve link bi xoa return tempLink; } //kiem tra list co trong hay khong bool isEmpty() Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 { return head == NULL; } int length() { int length = 0; struct node *current; for(current = head; current != NULL; current = current->next) { length++; } return length; } Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 //tim mot link voi key da cho struct node* find(int key){ //bat dau tim tu first link struct node* current = head; //neu list la trong if(head == NULL) { return NULL; } //duyet qua list while(current->key != key){ //neu day la last node if(current->next == NULL){ Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 return NULL; }else { //di chuyen toi next link current = current->next; } } //neu tim thay du lieu, tra ve link hien tai return current; } //xoa mot link voi key da cho struct node* deleteKey(int key){ //bat dau tu first link struct node* current = head; struct node* previous = NULL; Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 //neu list la trong if(head == NULL){ return NULL; } //duyet qua list while(current->key != key){ //neu day la last node if(current->next == NULL){ return NULL; }else { //luu tham chieu toi link hien tai previous = current; //di chuyen toi next link current = current->next; Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 } } //cap nhat link if(current == head) { //thay doi first de tro toi next link head = head->next; }else { //bo qua link hien tai previous->next = current->next; } return current; } // ham sap xep Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 void sort(){ int i, j, k, tempKey, tempData ; struct node *current; struct node *next; int size = length(); k = size ; for ( i = 0 ; i < size - 1 ; i++, k-- ) { current = head ; next = head->next ; for ( j = 1 ; j < k ; j++ ) { if ( current->data > next->data ) { tempData = current->data ; current->data = next->data; Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 next->data = tempData ; tempKey = current->key; current->key = next->key; next->key = tempKey; } current = current->next; next = next->next; } } } // ham dao nguoc list void reverse(struct node** head_ref) { struct node* prev = NULL; struct node* current = *head_ref; Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 struct node* next; while (current != NULL) { next = current->next; current->next = prev; prev = current; current = next; } *head_ref = prev; } main() { insertFirst(1,10); insertFirst(2,20); insertFirst(3,30); Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 insertFirst(4,1); insertFirst(5,40); insertFirst(6,56); printf("Danh sach ban dau: "); //in danh sach printList(); while(!isEmpty()){ struct node *temp = deleteFirst(); printf("\nGia tri bi xoa:"); printf("(%d,%d) ",temp->key,temp->data); } printf("\nDanh sach sau khi da xoa gia tri: "); printList(); Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 insertFirst(1,10); insertFirst(2,20); insertFirst(3,30); insertFirst(4,1); insertFirst(5,40); insertFirst(6,56); printf("\nPhuc hoi danh sach: "); printList(); printf("\n"); struct node *foundLink = find(4); if(foundLink != NULL){ printf("Tim thay phan tu: "); printf("(%d,%d) ",foundLink->key,foundLink->data); printf("\n"); }else { Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 printf("Khong tim thay phan tu."); } deleteKey(4); printf("Danh sach, sau khi xoa mot phan tu: "); printList(); printf("\n"); foundLink = find(4); if(foundLink != NULL){ printf("Tim thay phan tu: "); printf("(%d,%d) ",foundLink->key,foundLink->data); printf("\n"); }else { printf("Khong tim thay phan tu."); } Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 printf("\n"); sort(); printf("Danh sach sau khi duoc sap xep: "); printList(); reverse(&head); printf("\nDanh sach sau khi bi dao nguoc: "); printList(); } 1.

Các thao tác trên hàng đợi - Thao tác khởi tạo hàng đợi Thao tác này thực hiện việc gán giá trị 0 cho biến head, giá trị MAX -1 cho biến tail, và giá trị 0 cho biến count, cho biết hàng đợi đang ở trạng thái rỗng. void QueueInitialize(queue *q){ Downloaded by ANH LE (bachvan14@gmail.com) lOMoARcPSD|39108650 q-> head = 0; q-> tail = MAX-1; q-> count = 0; return; } - Thao tác kiểm tra hàng đợi rỗng Hàng đợi rỗng nếu có số phần tử nhỏ hơn hoặc bằng 0. int QueueEmpty(queue q){ return (q.count <= 0); } - Thao tác thêm 1 phần tử vào hàng đợi void Put(queue *q, int x){ if (q-> count == MAX) printf(“Hang doi day !”); else{ if (q->tail == MAX-1 ) q->tail=0; else (q->tail)++; q->node[q->tail]=x; q-> count++; Downloaded by ANH LE (bachvan14@gmail.

Nội dung được bảo vệ bản quyền — Tải xuống đầy đủ