Mỗi node quản lý thông tin cá nhân, các môn học: Toán, Vật lý, Tiếng anh, CTDLamp;GT. Trong đó môn học cũng là một cấu trúc gồm tên môn, số tín chỉ. Điểm cũng là một cấu trúc bao gồm Mã sinh viên, mã môn học. Nhập 1 danh sách cho đến khi nhập dấu “” vào tên đối tượng. 18.2. Sắp xếp danh sách theo chiều tăng dần của điểm toán. 18.3. Sắp xếp danh sách theo chiều tăng dần của điểm trung bình. 18.4 Tìm sinh viên có điểm trung bình MaxMin
Cấu trúc dữ liệu (Data Structure) là gì ?
Cấu trúc dữ liệu là phương pháp lưu trữ và tổ chức dữ liệu một cách có hệ thống và có thứ tự để đảm bảo việc truy cập và sử dụng dữ liệu trở nên hiệu quả hơn Việc áp dụng cấu trúc dữ liệu đúng đắn giúp tối ưu hóa hiệu suất và khả năng xử lý dữ liệu trong các hệ thống phần mềm và ứng dụng Do đó, hiểu rõ về các cấu trúc dữ liệu là yếu tố then chốt trong lập trình và phát triển công nghệ thông tin.
Dưới đây là hai khái niệm nền tảng hình thành nên một cấu trúc dữ liệu:
Mỗi cấu trúc dữ liệu đều có một Interface rõ ràng thể hiện tập hợp các phép tính hỗ trợ Interface này mô tả các phép toán mà cấu trúc dữ liệu đó có khả năng thực hiện, bao gồm danh sách các phép tính, loại tham số chấp nhận và kiểu dữ liệu trả về của từng phép tính Việc xác định rõ Interface giúp nâng cao tính mở rộng và khả năng sử dụng của các cấu trúc dữ liệu trong thiết kế phần mềm.
Trong bài viết này, chúng tôi nhấn mạnh rằng **Triển khai (Implementation) đề cập đến việc biểu diễn nội bộ của một cấu trúc dữ liệu**, giúp làm rõ cách dữ liệu được tổ chức và quản lý bên trong hệ thống Bên cạnh đó, **Implementation còn cung cấp phần định nghĩa của các thuật toán được sử dụng để thực hiện các phép tính trên cấu trúc dữ liệu đó**, đảm bảo hoạt động hiệu quả và chính xác.
-Đặc điểm của một Cấu trúc dữ liệu:
Chính xác: Sự triển khai của Cấu trúc dữ liệu nên triển khai Interface của nó một cách chính xác.
Độ phức tạp về thời gian (Time Complexity) đóng vai trò quan trọng trong hiệu suất của cấu trúc dữ liệu, yêu cầu thời gian thực thi của các phép tính phải được tối ưu hóa để đạt tốc độ nhanh nhất có thể Việc giảm thiểu thời gian xử lý giúp nâng cao hiệu quả vận hành và đáp ứng tốt hơn các yêu cầu của ứng dụng trong thực tế Chính vì vậy, tối ưu độ phức tạp về thời gian là mục tiêu hàng đầu trong thiết kế và lựa chọn cấu trúc dữ liệu phù hợp.
Độ phức tạp về bộ nhớ (Space Complexity): Sự sử dụng bộ nhớ của mỗi phép tính của cấu trúc dữ liệu nên là nhỏ nhất có thể.
Ngày nay, các ứng dụng trở nên phức tạp hơn với lượng dữ liệu ngày càng lớn và đa dạng về kiểu loại, đặt ra những thách thức lớn cho các lập trình viên Việc quản lý và xử lý dữ liệu hiệu quả đang trở thành vấn đề cốt lõi, đồng thời đảm bảo tính ổn định và bảo mật của hệ thống cũng là một yếu tố cần chú trọng Trong bối cảnh này, lập trình viên phải đối mặt với ba vấn đề chính liên quan đến tối ưu hóa hiệu suất, quản lý dữ liệu phức tạp và đảm bảo an toàn thông tin cho các ứng dụng ngày càng phát triển.
Việc tìm kiếm dữ liệu trong kho hàng có thể trở nên chậm và tốn kém khi lưu trữ số lượng lớn hàng hóa, ví dụ như một triệu mặt hàng Khi một ứng dụng cần tìm kiếm một mặt hàng cụ thể, nó phải quét qua toàn bộ danh mục để xác định vị trí, dẫn đến thời gian xử lý lâu hơn và chi phí cao hơn theo sự tăng trưởng của dữ liệu Do đó, tối ưu hóa phương pháp tìm kiếm là rất cần thiết để nâng cao hiệu quả quản lý kho khi quy mô dữ liệu ngày càng lớn.
Mặc dù bộ vi xử lý có tốc độ rất cao, nhưng vẫn có giới hạn về khả năng xử lý dữ liệu Khi lượng dữ liệu tăng lên đến hàng tỷ bản ghi, tốc độ xử lý của bộ vi xử lý sẽ giảm đáng kể Do đó, hiệu suất xử lý không còn nhanh như ban đầu khi đối mặt với khối lượng dữ liệu lớn.
Khi hàng nghìn người dùng cùng thực hiện các phép tìm kiếm trên một Web Server, việc xử lý đồng thời các yêu cầu này trở nên vô cùng thử thách dù server có hoạt động nhanh Để giải quyết vấn đề này, các cấu trúc dữ liệu đóng vai trò là giải pháp tối ưu, giúp tổ chức dữ liệu một cách hợp lý nhằm giảm thiểu thời gian tìm kiếm Nhờ đó, dữ liệu có thể được truy xuất một cách nhanh chóng và hiệu quả hơn, đảm bảo hiệu suất hoạt động của hệ thống trong tình huống tải cao.
1.1 Độ phức tạp thời gian thực thi trong cấu trúc dữ liệu và giải thuật:
Có 3 trường hợp thường được sử dụng để so sánh thời gian thực thi của các cấu trúc dữ liệu khác nhau:
Trong bài viết này, chúng ta sẽ tìm hiểu về trường hợp xấu nhất trong các cấu trúc dữ liệu, đó là tình huống mà một phép tính mất nhiều thời gian nhất có thể, gây ra hiệu suất tối đa Ví dụ, trong trường hợp tìm kiếm hoặc sắp xếp các số, thời gian xử lý sẽ kéo dài nhất khi xử lý các dữ liệu cụ thể như ba số 1, 2 Hiểu rõ về trường hợp xấu nhất giúp tối ưu hóa các thuật toán và cải thiện hiệu quả hoạt động của hệ thống dữ liệu.
Trong các trường hợp sắp xếp theo thứ tự giảm dần, thời gian thực thi sẽ kéo dài nhất, đây được xem là trường hợp xấu nhất Ngược lại, khi sắp xếp theo thứ tự tăng dần, thời gian thực thi sẽ ngắn nhất, tạo điều kiện cho hoạt động hiệu quả hơn và được coi là trường hợp tốt nhất Việc lựa chọn sắp xếp thích hợp đóng vai trò quan trọng trong tối ưu hóa hiệu suất của thuật toán.
Trường hợp trung bình (Average Case): miêu tả thời gian thực thi trung bình một phép tính của một cấu trúc dữ liệu.
Trường hợp tốt nhất trong phân tích dữ liệu cấu trúc đề cập đến tình huống mà thời gian thực thi của một phép tính là ngắn nhất có thể, giúp tối ưu hiệu suất xử lý Ví dụ, trong một số cấu trúc dữ liệu như cây tìm kiếm, truy cập dữ liệu trong trường hợp tốt nhất có thể diễn ra nhanh chóng khi dữ liệu đã được sắp xếp sẵn Hiểu rõ về trường hợp tốt nhất giúp các nhà phát triển tối ưu hóa mã nguồn và cải thiện hiệu quả của các ứng dụng phần mềm.
-Thuật ngữ cơ bản trong Cấu trúc dữ liệu:
Dữ liệu: Dữ liệu là các giá trị hoặc là tập hợp các giá trị.
Phần tử dữ liệu: Phần tử dữ liệu là một đơn vị đơn lẻ của giá trị.
Các phần tử nhóm: Phần tử dữ liệu mà được chia thành các phần tử con thì được gọi là các phần tử nhóm.
Các phần tử cơ bản: Phần tử dữ liệu mà không thể bị chia nhỏ thành các phần tử con thì gọi là các phần tử cơ bản.
Trong lập trình và cơ sở dữ liệu, thực thể (entity) được định nghĩa là một đối tượng có thể chứa nhiều thuộc tính (attributes), và các thuộc tính này có thể được gán các giá trị khác nhau Thuộc tính giúp mô tả đặc điểm của thực thể, góp phần xác định và phân loại chúng một cách rõ ràng Việc hiểu rõ mối quan hệ giữa thuộc tính và thực thể là yếu tố then chốt trong việc thiết kế cấu trúc dữ liệu hiệu quả và chính xác trong các hệ thống thông tin.
Tập hợp thực thể: Các thực thể mà có các thuộc tính tương tự nhau thì cấu thành một tập hợp thực thể.
Trường: Trường là một đơn vị thông tin cơ bản biểu diễn một thuộc tính của một thực thể.
Bản ghi: Bản ghi là một tập hợp các giá trị trường của một thực thể đã cho.
File: Là một tập hợp các bản ghi của các thực thể trong một tập hợp thực thể đã cho.
Giải thuật là gì ?
Giải thuật, còn gọi là thuật toán (Algorithms trong tiếng Anh), là tập hợp hữu hạn các chỉ thị được thực thi theo một thứ tự nhất định để đạt được kết quả mong muốn Giải thuật hoạt động độc lập với các ngôn ngữ lập trình, cho phép được triển khai trong nhiều ngôn ngữ lập trình khác nhau.
Xuất phát từ quan điểm của cấu trúc dữ liệu, dưới đây là một số giải thuật quan trọng:
Giải thuật Tìm kiếm: Giải thuật để tìm kiếm một phần tử trong một cấu trúc dữ liệu.
Giải thuật Sắp xếp: Giải thuật để sắp xếp các phần tử theo thứ tự nào đó.
Giải thuật Chèn: Giải thuật để chèn phần từ vào trong một cấu trúc dữ liệu.
Giải thuật Cập nhật: Giải thuật để cập nhật (hay update) một phần tử đã tồn tại trong một cấu trúc dữ liệu.
Giải thuật Xóa: Giải thuật để xóa một phần tử đang tồn tại từ một cấu trúc dữ liệu.
-Đặc điểm của giải thuật:
Không phải tất cả các thủ tục có thể được gọi là một giải thuật Một giải thuật nên có các đặc điểm sau:
Trong quá trình xây dựng giải thuật, tính xác định đóng vai trò quan trọng để đảm bảo quá trình diễn ra rõ ràng và chính xác Mỗi giai đoạn hay bước trong thuật toán cần phải rõ ràng, tránh sự mơ hồ để dễ dàng hiểu và thực hiện Việc xác định rõ mục đích của từng bước giúp tăng tính hiệu quả và đảm bảo kết quả cuối cùng đúng như mong đợi, góp phần nâng cao chất lượng của giải thuật trong các ứng dụng thực tiễn.
Dữ liệu đầu vào xác định: Một giải thuật nên có 0 hoặc nhiều hơn dữ liệu đầu vào đã xác định.
Trong quá trình thiết kế giải thuật, kết quả đầu ra đóng vai trò quan trọng và cần xác định rõ ràng Một giải thuật hiệu quả phải có ít nhất một hoặc nhiều dữ liệu đầu ra phù hợp, phản ánh chính xác kết quả mong đợi Ngoài ra, các dữ liệu đầu ra này cần liên kết chặt chẽ với kiểu kết quả bạn mong muốn để đảm bảo tính chính xác và hiệu quả của thuật toán Việc xác định rõ kết quả đầu ra giúp tối ưu hóa quá trình xử lý và nâng cao hiệu suất của giải thuật trong các ứng dụng thực tế.
Tính dừng: Các giải thuật phải kết thúc sau một số hữu hạn các bước.
Hiệu quả của một thuật toán là yếu tố quyết định, đảm bảo rằng nó có thể thực thi dễ dàng với các nguồn lực sẵn có, đồng thời giải quyết vấn đề một cách nhanh chóng và tối ưu trong phạm vi thời gian và tài nguyên cho phép.
Tính phổ biến: Một giải thuật có tính phổ biến nếu giải thuật này có thể giải quyết được một lớp các vấn đề tương tự.
Độc lập: Một giải thuật nên có các chỉ thị độc lập với bất kỳ phần code lập trình nào.
Cách viết một giải thuật ?
Bạn không cần phải tìm kiếm các tiêu chuẩn cố định để viết giải thuật, vì không có một chuẩn nào được quy định trước Các ngôn ngữ lập trình phổ biến đều hỗ trợ các cấu trúc như vòng lặp (do, for, while) và lệnh điều khiển luồng (if-else) để giúp bạn xây dựng các giải thuật linh hoạt Bạn có thể sử dụng những cấu trúc này để viết ra các giải thuật phù hợp với yêu cầu của mình một cách dễ dàng và hiệu quả.
Viết giải thuật là một quá trình theo từng bước rõ ràng sau khi xác định chính xác vấn đề cần giải quyết Quá trình này bắt đầu từ việc định vị vấn đề, sau đó thiết kế giải pháp phù hợp để xử lý vấn đề đó Cuối cùng, chúng ta mới tiến hành viết và triển khai giải thuật, đảm bảo tính logic và hiệu quả trong quá trình giải quyết vấn đề.
-Ví dụ viết giải thuật:
Bạn theo dõi ví dụ minh họa dưới đây để hiểu rõ các bước và cách viết một giải thuật phù hợp Ví dụ này được thiết kế đơn giản để giúp bạn dễ dàng nắm bắt quy trình, đặc biệt phù hợp cho những người mới bắt đầu học viết giải thuật Việc giữ cho ví dụ ngắn gọn và dễ hiểu sẽ giúp bạn tự tin hơn trong việc áp dụng kiến thức vào các bài toán thực tế, từ đó nâng cao kỹ năng lập trình và phát triển thuật toán hiệu quả.
Bài toán: Thiết kế một giải thuật để cộng hai số và hiển thị kết quả.
Bước 2: Khai báo ba số a, b & c
Bước 3: Định nghĩa các giá trị cua a & b
Bước 4: Cộng các giá trị cua a & b
Bước 5: Lưu trữ kết qua cua Bước 4 vào biến c
Các giải thuật nói cho lập trình viên cách để viết code Ngoài ra, bạn cũng có thể viết một giải thuật cho bài toán trên như sau:
Bước 2: Lầy giá trị cua a & b
Trong quá trình thiết kế và phân tích các giải thuật, phương pháp thứ hai thường được sử dụng để mô tả giải thuật một cách rõ ràng và dễ hiểu Phương pháp này giúp đơn giản hóa việc phân tích bằng cách bỏ qua các phần định nghĩa không cần thiết, từ đó làm rõ các phép tính và bước tiến trình thực thi của giải thuật Nhìn vào cách trình bày này, người đọc có thể dễ dàng nhận biết các hoạt động chính của giải thuật, hỗ trợ quá trình tối ưu hóa và đánh giá hiệu quả của giải pháp.
Hiệu quả của một giải thuật có thể được phân tích dựa trên 2 góc độ: trước khi triển khai và sau khi triển khai:
Phân tích lý thuyết là phương pháp đánh giá hiệu quả của giải thuật dựa trên các giả thuyết lý thuyết, trong đó tất cả các yếu tố khác như tốc độ vi xử lý được coi là cố định và không ảnh hưởng đến kết quả triển khai Phương pháp này giúp xác định chính xác khả năng hoạt động của giải thuật trong điều kiện lý tưởng, cung cấp cái nhìn tổng quan về tính tối ưu của nó.
Phân tích tiệm cận là bước quan trọng sau khi đã triển khai giải thuật trên một ngôn ngữ lập trình cụ thể, giúp đánh giá hiệu quả của thuật toán dựa trên các tiêu chí như thời gian chạy, thời gian thực thi và lượng bộ nhớ tiêu thụ Quá trình này bao gồm việc chạy thử và đo lường các thông số liên quan để đảm bảo thuật toán hoạt động tối ưu và phù hợp với yêu cầu đề bài, từ đó nâng cao chất lượng và độ tin cậy của giải pháp.
Chương này chúng ta sẽ tìm hiểu phân tích lý thuyết Còn phân tích tiệm cận chúng ta sẽ cùng tìm hiểu ở chương tiếp theo.
2.2 Độ phức tạp giải thuật (Algorithm Complexity):
Độ phức tạp giải thuật chính là hàm ước lượng số phép tính cần thiết để thực hiện giải thuật trên bộ dữ liệu đầu vào có kích thước n Điều này giúp chúng ta dễ dàng xác định thời gian thực hiện của giải thuật, dựa trên kích thước n của dữ liệu Trong đó, n có thể đại diện cho số phần tử của mảng trong các bài toán sắp xếp hoặc tìm kiếm, hoặc là độ lớn của số trong các bài toán kiểm tra số nguyên tố Việc đánh giá độ phức tạp giúp tối ưu hóa hiệu năng và lựa chọn giải pháp phù hợp cho từng vấn đề.
Khi đánh giá một giải thuật X, kích cỡ dữ liệu đầu vào n đóng vai trò quan trọng ảnh hưởng đến hiệu quả của giải thuật Thời gian thực thi và lượng bộ nhớ tiêu thụ là hai yếu tố chính quyết định tính khả thi và tối ưu của giải thuật X Hiểu rõ mối liên hệ giữa độ phức tạp thời gian và bộ nhớ giúp tối ưu hóa hiệu suất của giải thuật trên dữ liệu lớn Chính vì vậy, việc phân tích và tối ưu các yếu tố này là điều cần thiết để đảm bảo giải thuật hoạt động hiệu quả nhất trên dữ liệu đầu vào có kích cỡ n.
Nhân tố thời gian: Thời gian được đánh giá bằng việc tính số phép tính chính
(chẳng hạn như các phép so sánh trong thuật toán sắp xếp).
Yếu tố bộ nhớ là lượng bộ nhớ tối đa mà thuật toán yêu cầu để hoạt động hiệu quả Độ phức tạp của thuật toán, thường được biểu diễn bằng hàm f(n), thể hiện mối quan hệ giữa thời gian chạy và lượng bộ nhớ tiêu thụ trong quá trình thực thi Hiểu rõ về bộ nhớ sử dụng giúp tối ưu hóa hiệu suất và lựa chọn thuật toán phù hợp cho các bài toán lớn.
2.3 Độ phức tạp bộ nhớ (Space complexity) trong phân tích giải thuật:
Nhân tố bộ nhớ của một giải thuật thể hiện lượng bộ nhớ cần thiết trong suốt vòng đời hoạt động của giải thuật Lượng bộ nhớ cần sử dụng, ký hiệu là S(P), được xác định là tổng của hai thành phần chính Việc hiểu rõ yếu tố này giúp tối ưu hóa hiệu suất và tính khả thi của giải thuật trong các ứng dụng thực tế.
Phần cố định, hay còn gọi là phần C, là lượng bộ nhớ cần thiết để lưu trữ dữ liệu và các biến nhất định, và phần này không phụ thuộc vào kích thước của bài toán Ví dụ điển hình bao gồm các biến và hằng số đơn giản, giúp tối ưu bộ nhớ trong quá trình xử lý dữ liệu.
Cấu trúc dữ liệu mảng là gì ?
Mảng (Array) là một trong những cấu trúc dữ liệu cơ bản và quan trọng nhất, giúp lưu trữ nhiều phần tử cố định cùng kiểu dữ liệu Các phần tử trong mảng có thể truy cập dễ dàng và nhanh chóng, là nền tảng cho nhiều giải thuật và cấu trúc dữ liệu phức tạp hơn Hiểu rõ về mảng là chìa khóa để xây dựng các ứng dụng tối ưu và hiệu quả, đồng thời là kiến thức nền tảng trong lập trình và phát triển phần mềm.
Phần tử: Mỗi mục được lưu giữ trong một mảng được gọi là một phần tử.
Chỉ mục (Index): Mỗi vị trí của một phần tử trong một mảng có một chỉ mục số được sử dụng để nhận diện phần tử.
-Mảng gồm các bản ghi có kiểu giống nhau, có kích thước cố định, mỗi phần tử được xác định bởi chỉ số.
-Mảng là cấu trúc dữ liệu được cấp phát lien tục cơ bản. Ưu điểm của mảng :
-Truy câp phàn tử vơi thời gian hằng số O(1).
-Sử dụng bộ nhớ hiệu quả.
-Tính cục bộ về bộ nhớ.
-Không thể thay đổi kích thước của mảng khi chương trình dang thực hiện.
-Mảng động (dynamic aray) : cấp phát bộ nhớ cho mảng một cách động trong quá trình chạy chương trình trong C là malloc và calloc, trong C++ là new.
Sử dụng mảng động bắt đầu với một phần tử, giúp dễ dàng mở rộng khi số lượng phần tử tăng lên Khi vượt quá khả năng chứa của mảng ban đầu, ta sẽ gấp đôi kích thước mảng và sao chép các phần tử cũ vào nửa đầu của mảng mới để đảm bảo dữ liệu được lưu trữ hiệu quả Quản lý mảng động là kỹ thuật tối ưu trong lập trình để xử lý dữ liệu linh hoạt, phù hợp với các ứng dụng cần mở rộng dữ liệu một cách linh hoạt và hiệu quả.
-Ưu điểm : tránh lãng phí bộ nhớ khi phải khai báo mảng có kích thước lớn ngay từ đầu.
Nhược điểm chính của phương pháp này là người dùng phải thực hiện thêm bước sao chép phần tử mỗi khi thay đổi kích thước, gây lưu ý về tính tiện lợi Đồng thời, trong một số trường hợp, thời gian thực hiện các thao tác này không còn duy trì ổn định như ban đầu, ảnh hưởng đến hiệu suất làm việc và trải nghiệm người dùng.
3.1 Biểu diễn Cấu trúc dữ liệu mảng:
Trong các ngôn ngữ lập trình, mảng có thể được khai báo theo nhiều cách khác nhau tùy thuộc vào ngữ cảnh sử dụng Ví dụ, trong ngôn ngữ C, việc khai báo mảng rất phổ biến và được thực hiện bằng cú pháp cụ thể để xác định loại dữ liệu và kích thước của mảng Việc hiểu rõ các phương pháp khai báo mảng trong C giúp lập trình viên tối ưu hóa khả năng quản lý bộ nhớ và xử lý dữ liệu hiệu quả hơn Bên cạnh đó, nắm vững cách khai báo mảng cũng là nền tảng để làm việc với các cấu trúc dữ liệu phức tạp hơn trong lập trình.
Hình1: minh họa phần tử và chỉ mục:
Hình 2: minh họa phần tử và chỉ mục tiêu
Dưới đây là một số điểm cần ghi nhớ về cấu trúc dữ liệu mảng:
Chỉ mục bắt đầu với 0.
Độ dài mảng là 10, nghĩa là mảng có thể lưu giữ 10 phần tử.
Trong lập trình, mỗi phần tử trong mảng đều có thể được truy cập dễ dàng thông qua chỉ mục của nó Ví dụ, bạn có thể lấy giá trị của phần tử tại chỉ mục 6 là 27, giúp thao tác dữ liệu trở nên nhanh chóng và hiệu quả hơn Việc sử dụng chỉ mục để truy cập phần tử là nguyên tắc cơ bản và quan trọng trong quản lý mảng, góp phần tối ưu hóa quá trình xử lý dữ liệu trong các chương trình phần mềm.
Phép toán cơ bản được hỗ trợ bởi mảng:
Dưới đây là các hoạt động cơ bản được hỗ trợ bởi một mảng:
Duyệt: In tất cả các phần tử mảng theo cách in từng phần tử một.
Chèn: Thêm một phần tử vào mảng tại chỉ mục đã cho.
Xóa: Xóa một phần tử từ mảng tại chỉ mục đã cho.
Tìm kiếm: Tìm kiếm một phần tử bởi sử dụng chỉ mục hay bởi giá trị.
Cập nhật: Cập nhật giá trị một phần tử tại chỉ mục nào đó.
Trong ngôn ngữ C, khi một mảng được khởi tạo với kích thước ban đầu, các phần tử của mảng sẽ được gán giá trị mặc định dựa trên kiểu dữ liệu của chúng Đối với các kiểu dữ liệu cơ bản như int, float, và double, các phần tử sẽ được tự động khởi tạo về giá trị 0 hoặc 0.0 tương ứng Điều này giúp đảm bảo rằng mảng không chứa dữ liệu ngẫu nhiên khi được khởi tạo, tạo điều kiện thuận lợi cho lập trình viên trong việc xử lý dữ liệu Hiểu rõ cách khởi tạo mảng trong C giúp tối ưu hóa quá trình lập trình và kiểm soát dữ liệu hiệu quả hơn.
Kiểu dữ liệu Giá trị mặc định bool False
3.2 Hoạt động chèn phần tử vào mảng:
Hoạt động chèn trong lập trình là quá trình thêm một hoặc nhiều phần tử dữ liệu vào trong mảng, giúp mở rộng hoặc cập nhật nội dung của mảng Tùy thuộc vào yêu cầu, phần tử mới có thể được chèn vào vị trí đầu tiên, cuối cùng hoặc tại bất kỳ vị trí chỉ mục nào đã xác định trong mảng, đảm bảo tính linh hoạt trong quản lý dữ liệu.
Trong phần tiếp theo, chúng ta sẽ cùng triển khai hoạt động chèn dữ liệu trong một ví dụ thực tế Cụ thể, chúng ta sẽ hướng dẫn cách chèn dữ liệu vào cuối mảng để hiểu rõ quá trình thao tác và ứng dụng trong lập trình Các bước thực hiện sẽ giúp bạn nắm vững kỹ năng thêm phần tử mới vào mảng một cách dễ dàng và chính xác.
Giả sử LA là một mảng tuyến tính không có thứ tự gồm N phần tử và K là một số nguyên dương thỏa mãn K ≤ N Thuật toán chèn phần tử A vào vị trí thứ K trong mảng giúp tối ưu quá trình cập nhật dữ liệu, đảm bảo tính chính xác và hiệu quả Quá trình này bao gồm các bước cụ thể để đảm bảo phần tử A được chèn đúng vị trí, đồng thời duy trì tính liên tục của mảng Áp dụng thuật toán này không những giúp nâng cao hiệu suất xử lý dữ liệu mà còn đảm bảo tính linh hoạt trong quản lý mảng tuyến tính không sắp xếp Đây là phương pháp quan trọng trong lập trình để thao tác dữ liệu hiệu quả và tối ưu hóa các hoạt động xử lý mảng trong các ứng dụng thực tế.
4 Lặp lại bước 5 và 6 khi J >= K
Sau đây là code đầy đủ của giải thuật trên trong ngôn ngữ C:
#include main() { int LA[] = {1,3,5,7,8}; int item = 10, k = 3, n = 5; int i = 0, j = n; printf("Danh sach phan tu trong mang ban dau:\n"); for(i = 0; inext = NULL là đã xóa được node cuối cùng.
7.6.3 Hàm xóa sinh viên ở vị trí bất kì
Hình 18: code hàm xóa phần tử ở vị trí bất kì.
- Đầu tiên kiểm tra vị trí cần nhập có hợp lệ không.
- tạo một node truoc = NULL, node sau = a;
- Sử dụng vòng for int i = 1, i < pos; i++; : Để node truoc và sau chạy đến đúng vị trí cần xóa.
- Tác dụng của node truoc giống như ở Hàm xóa phần tử ở cuối.
- Sau đó ta chỉ cần gán node truoc->next = sau->next để bỏ qua node sau là đã xóa thành công.
7.7 Khởi tạo hàm in thông tin sinh viên, thông tin môn học và hàm in danh sách sinh viên, thông tin môn học.
- void in(SV s) : Hàm in thông tin sinh viên
- void in_mh(SV s): Hàm in thông tin môn học
- Hàm in là hàm cơ bản đã được học từ khi nhập môn lập trình nên em xin phép không trình bày vào.
- Hàm in danh sách thông tin sinh viên:
Hình 19: code hàm in danh sách thông tin sinh viên.
- Hàm in danh sách thông tin môn học:
Hình 20: code hàm in danh sách thông tin môn học.
- Hài hàm nay tương tự nhau ta sử dụng vòng lặp while với điều kiện node a ! NULL.
- Dòng đầu là để tính điểm trung bình.
Trong quá trình xử lý dữ liệu, sau đó gọi hàm in thông tin sinh viên để hiển thị danh sách sinh viên, đồng thời gọi hàm in danh sách thông tin môn học để hiển thị các môn học Các hàm này giúp tổ chức và trình bày dữ liệu một cách rõ ràng, thuận tiện cho việc quản lý thông tin Việc sử dụng các hàm riêng biệt cho sinh viên và môn học đảm bảo mã nguồn rõ ràng, dễ bảo trì và nâng cấp Điều này cũng giúp dễ dàng mở rộng chức năng trong quá trình phát triển phần mềm quản lý học tập.
- Sau đó cho a = a-> next để in node tiếp theo.
Hình 21: kết quả code hàm in danh sách thông tin sinh viên.
Hình 22: kết quả code hàm in danh sách thông tin môn học.
7.8 Khởi tạo Hàm sắp xếp điểm toán theo thứ tự tăng dần và điểm trung bình theo thứ tự tăng dần.
* Sử dụng pp selection short.
- Hàm sắp xếp điểm toán tăng dần:
Hình 23: code hàm sắp xếp điểm toán tăng dần.
- Hàm sắp xếp điểm trung bình tăng dần:
Hình 24: code hàm sắp xếp điểm trung bình tăng dần.
- 2 Hàm này tương tự nhau chỉ thay điều kiện j->data.y.diem1 thành j-
- Sử dùng vòng lặp for lồng nhau node i và node j.
- Vòng ngoài node i = a; i !=NULL; I = i->next.
- Vòng trong node j = i->next; j !=NULL; I = j->next.
- Ở vòng ngoài tạo một biến node min = i;
- Ở vòng trong ta kiểm tra điều kiện nếu j->data.y.diemTB mà nhỏ hơn min-> data.y.diemTB thì gán min = j.
Sau đó ra vòng ngoài ta gọi 1 struct SV có tên tmp (là biến tạm) để thực hiện đổi chỗ min->data với i->data.
Hình 25: code hàm sắp xếp điểm toán tăng dần.
Hình 26: code hàm sắp xếp điểm trung bình tăng dần
7.9 Khởi tạo hàm tìm sinh viên có điểm trung bình lớn nhất và bé nhất
- Hàm tìm sinh viên điểm trung bình lớn nhất:
Hình 27: code hàm tìm sinh viên có điểm trung bình lớn nhất.
- Tạo một node x thế node a;
- Tạo SV có tên max = x trỏ tới data.
- Ta sử dụng vòng for cho node i = a; i->next != NULL; i = i->next
- cho x = x->next (để chạy lần lượt từng node sau mỗi vòng lặp)
- Kiểm tra nếu max.y.diemTB < x->data.y.diemTB.
- Nếu bé hơn thì gắn max.y.diemTB = x->data.y.diemTB.
- Sau đó gọi hàm in(max);
Hình 28:kết quả code hàm tìm sinh viên có điểm trung bình lớn nhất.
- Hàm tìm sinh viên điểm trung bình nhỏ nhất:
Hình 29: code hàm tìm sinh viên có điểm trung bình nhỏ nhất.
- Tạo một node y thế node a;
- Tạo SV có tên min = y trỏ tới data.
- Thuật toán tương tự như tìm max chỉ thay đổi điều kiện bé hơn thành lớn hơn.
Hình 30: kết quả code hàm tìm sinh viên có điểm trung bình nhỏ nhất.
- Khai báo node hssv = NULL;
- In một menu có các thông tin từng chức năng.
- khai báo biến lc có kiểu int cin >> lc; để nhập lựa chọn.
- Sử dụng if ở từng if ta gắn từng hàm tương ứng là xong.
Hình 32: kết quả tạo menu.