The course provides a comprehensive introduction to the design and analysis of algorithms at the postgraduate level. It prepares students, who have not had an introduction to this topic for advanced studies of most computer science subjects, such as artificial intelligence and data science. The ultimate aim of this course is to give students the skill to create computer code that can provably and efficiently solve every instance of a given problem. For this purpose, an in-depth understanding is required of why the algorithm works correctly and does the best job it can. Proof techniques, such as direct proofs, proofs by contraposition or contradiction, and induction principles are discussed. Algorithmic design paradigms such as greedy, divide-and-conquer, and dynamic programming are explained in general and with concrete examples. An elementary toolbox is established for analysing the worst-case and average-case resources in terms of time and space required by an algorithm. Data structures, such as records, lists, graphs, hash tables, priority queues, and trees, will be used to implement algorithms efficiently. An opportunity is provided to delve into deeper topics of algorithmics, including probabilistic, parallel, or approximate algorithms, or algorithmic complexity theory. The latter provides principled methods in showing the infeasibility of algorithmic solutions, or the likely intractability of computational problems.