100% found this document useful (3 votes)
30 views

Instant download of Data Structures and Algorithms Using Python 1st Edition Rance D. Necaise ebook PDF, every chapter

The document provides information on downloading the ebook 'Data Structures and Algorithms Using Python' by Rance D. Necaise, along with links to other related ebooks. It includes details about the book's content, structure, and publication information. The document also lists suggested products for further reading in the field of data structures and algorithms.

Uploaded by

calvomeasor
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (3 votes)
30 views

Instant download of Data Structures and Algorithms Using Python 1st Edition Rance D. Necaise ebook PDF, every chapter

The document provides information on downloading the ebook 'Data Structures and Algorithms Using Python' by Rance D. Necaise, along with links to other related ebooks. It includes details about the book's content, structure, and publication information. The document also lists suggested products for further reading in the field of data structures and algorithms.

Uploaded by

calvomeasor
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 78

Visit https://ebookultra.

com to download the full version and


explore more ebooks

Data Structures and Algorithms Using Python 1st


Edition Rance D. Necaise

_____ Click the link below to download _____


https://ebookultra.com/download/data-structures-and-
algorithms-using-python-1st-edition-rance-d-necaise/

Explore and download more ebooks at ebookultra.com


Here are some suggested products you might be interested in.
Click the link to download

Python for Everyone 2nd Edition Cay Horstmann. Rance


Necaise

https://ebookultra.com/download/python-for-everyone-2nd-edition-cay-
horstmann-rance-necaise/

Growing Algorithms and Data Structures 4th Edition David


Scuse

https://ebookultra.com/download/growing-algorithms-and-data-
structures-4th-edition-david-scuse/

Learning F Functional Data Structures and Algorithms 1st


Edition Masood

https://ebookultra.com/download/learning-f-functional-data-structures-
and-algorithms-1st-edition-masood/

Data Structures Algorithms In Go 1st Edition Hemant Jain

https://ebookultra.com/download/data-structures-algorithms-in-go-1st-
edition-hemant-jain/
Data structures using C 1st Edition Patil

https://ebookultra.com/download/data-structures-using-c-1st-edition-
patil/

Learning JavaScript Data Structures and Algorithms 2nd


Edition Loiane Groner

https://ebookultra.com/download/learning-javascript-data-structures-
and-algorithms-2nd-edition-loiane-groner/

Concise Notes on Data Structures and Algorithms Ruby


Edition Christopher Fox

https://ebookultra.com/download/concise-notes-on-data-structures-and-
algorithms-ruby-edition-christopher-fox/

Data Structures and Algorithms in Java 4th Edition Michael


T. Goodrich

https://ebookultra.com/download/data-structures-and-algorithms-in-
java-4th-edition-michael-t-goodrich/

Data Structures and Algorithms in Java 6th Edition Michael


T. Goodrich

https://ebookultra.com/download/data-structures-and-algorithms-in-
java-6th-edition-michael-t-goodrich/
Data Structures and Algorithms Using Python 1st Edition
Rance D. Necaise Digital Instant Download
Author(s): Rance D. Necaise
ISBN(s): 9780470618295, 0470618299
Edition: 1
File Details: PDF, 10.19 MB
Year: 2010
Language: english
This page intentionally left blank
Data Structures and
Algorithms Using
Python

Rance D. Necaise
Department of Computer Science
College of William and Mary

JOHN WILEY & SONS, INC.


ACQUISITIONS EDITOR Beth Golub
MARKETING MANAGER Christopher Ruel
EDITORIAL ASSISTANT Michael Berlin
SENIOR DESIGNER Jeof Vita
MEDIA EDITOR Thomas Kulesa
PRODUCTION MANAGER Micheline Frederick
PRODUCTION EDITOR Amy Weintraub

This book was printed and bound by Hamilton Printing Company. The cover was
printed by Hamilton Printing Company

This book is printed on acid free paper. ∞

Copyright ©2011 John Wiley & Sons, Inc. All rights reserved. No part of this
publication may be reproduced, stored in a retrieval system or transmitted in any
form or by any means, electronic, mechanical, photocopying, recording, scanning or
otherwise, except as permitted under Sections 107 or 108 of the 1976 United States
Copyright Act, without either the prior written permission of the Publisher, or
authorization through payment of the appropriate per-copy fee to the Copyright
Clearance Center, Inc. 222 Rosewood Drive, Danvers, MA 01923, website
www.copyright.com. Requests to the Publisher for permission should be addressed
to the Permissions Department, John Wiley & Sons, Inc., 111 River Street, Hoboken,
NJ 07030-5774, (201)748-6011, fax (201)748-6008, website
http://www.wiley.com/go/permissions.

“Evaluation copies are provided to qualified academics and professionals for review
purposes only, for use in their courses during the next academic year. These copies
are licensed and may not be sold or transferred to a third party. Upon completion
of the review period, please return the evaluation copy to Wiley. Return
instructions and a free of charge return shipping label are available at
www.wiley.com/go/returnlabel. Outside of the United States, please contact your
local representative.”

Library of Congress Cataloging-in-Publication Data

Necaise, Rance D.
Data structures and algorithms using Python / Rance D. Necaise.
p. cm.
Includes bibliographical references and index.
ISBN 978-0-470-61829-5 (pbk.)
1. Python (Computer program language) 2. Algorithms.
3. Data structures (Computer science) I. Title.
QA76.73.P98N43 2011
005.13'3—dc22 2010039903

Printed in the United States of America

10 9 8 7 6 5 4 3 2 1
To my nieces and nephews
Allison, Janey, Kevin, RJ, and Maria
This page intentionally left blank
Contents

Preface xiii

Chapter 1: Abstract Data Types 1


1.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.1 Abstractions . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.1.2 Abstract Data Types . . . . . . . . . . . . . . . . . . . . . . 3
1.1.3 Data Structures . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1.4 General Definitions . . . . . . . . . . . . . . . . . . . . . . . 6
1.2 The Date Abstract Data Type . . . . . . . . . . . . . . . . . . . . . 7
1.2.1 Defining the ADT . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.2 Using the ADT . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2.3 Preconditions and Postconditions . . . . . . . . . . . . . . . 9
1.2.4 Implementing the ADT . . . . . . . . . . . . . . . . . . . . . 10
1.3 Bags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.3.1 The Bag Abstract Data Type . . . . . . . . . . . . . . . . . 15
1.3.2 Selecting a Data Structure . . . . . . . . . . . . . . . . . . 17
1.3.3 List-Based Implementation . . . . . . . . . . . . . . . . . . 19
1.4 Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.4.1 Designing an Iterator . . . . . . . . . . . . . . . . . . . . . 21
1.4.2 Using Iterators . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.5 Application: Student Records . . . . . . . . . . . . . . . . . . . . . 23
1.5.1 Designing a Solution . . . . . . . . . . . . . . . . . . . . . . 23
1.5.2 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . 26
Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Programming Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

Chapter 2: Arrays 33
2.1 The Array Structure . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.1.1 Why Study Arrays? . . . . . . . . . . . . . . . . . . . . . . . 34
2.1.2 The Array Abstract Data Type . . . . . . . . . . . . . . . . . 34
2.1.3 Implementing the Array . . . . . . . . . . . . . . . . . . . . 36
2.2 The Python List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

v
vi CONTENTS

2.2.1 Creating a Python List . . . . . . . . . . . . . . . . . . . . . 41


2.2.2 Appending Items . . . . . . . . . . . . . . . . . . . . . . . . 42
2.2.3 Extending A List . . . . . . . . . . . . . . . . . . . . . . . . 44
2.2.4 Inserting Items . . . . . . . . . . . . . . . . . . . . . . . . . 44
2.2.5 List Slice . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.3 Two-Dimensional Arrays . . . . . . . . . . . . . . . . . . . . . . . . 47
2.3.1 The Array2D Abstract Data Type . . . . . . . . . . . . . . . 47
2.3.2 Implementing the 2-D Array . . . . . . . . . . . . . . . . . . 49
2.4 The Matrix Abstract Data Type . . . . . . . . . . . . . . . . . . . . 52
2.4.1 Matrix Operations . . . . . . . . . . . . . . . . . . . . . . . 53
2.4.2 Implementing the Matrix . . . . . . . . . . . . . . . . . . . . 55
2.5 Application: The Game of Life . . . . . . . . . . . . . . . . . . . . . 57
2.5.1 Rules of the Game . . . . . . . . . . . . . . . . . . . . . . . 57
2.5.2 Designing a Solution . . . . . . . . . . . . . . . . . . . . . . 59
2.5.3 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . 61
Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
Programming Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

Chapter 3: Sets and Maps 69


3.1 Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
3.1.1 The Set Abstract Data Type . . . . . . . . . . . . . . . . . . 70
3.1.2 Selecting a Data Structure . . . . . . . . . . . . . . . . . . 72
3.1.3 List-Based Implementation . . . . . . . . . . . . . . . . . . 72
3.2 Maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
3.2.1 The Map Abstract Data Type . . . . . . . . . . . . . . . . . 76
3.2.2 List-Based Implementation . . . . . . . . . . . . . . . . . . 77
3.3 Multi-Dimensional Arrays . . . . . . . . . . . . . . . . . . . . . . . 80
3.3.1 The MultiArray Abstract Data Type . . . . . . . . . . . . . . 81
3.3.2 Data Organization . . . . . . . . . . . . . . . . . . . . . . . 81
3.3.3 Variable-Length Arguments . . . . . . . . . . . . . . . . . . 85
3.3.4 Implementing the MultiArray . . . . . . . . . . . . . . . . . . 86
3.4 Application: Sales Reports . . . . . . . . . . . . . . . . . . . . . . 89
Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
Programming Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96

Chapter 4: Algorithm Analysis 97


4.1 Complexity Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . 97
4.1.1 Big-O Notation . . . . . . . . . . . . . . . . . . . . . . . . . 99
4.1.2 Evaluating Python Code . . . . . . . . . . . . . . . . . . . . 104
4.2 Evaluating the Python List . . . . . . . . . . . . . . . . . . . . . . . 108
4.3 Amortized Cost . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
4.4 Evaluating the Set ADT . . . . . . . . . . . . . . . . . . . . . . . . 113
CONTENTS vii

4.5 Application: The Sparse Matrix . . . . . . . . . . . . . . . . . . . . 115


4.5.1 List-Based Implementation . . . . . . . . . . . . . . . . . . 115
4.5.2 Efficiency Analysis . . . . . . . . . . . . . . . . . . . . . . . 120
Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
Programming Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122

Chapter 5: Searching and Sorting 125


5.1 Searching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
5.1.1 The Linear Search . . . . . . . . . . . . . . . . . . . . . . . 126
5.1.2 The Binary Search . . . . . . . . . . . . . . . . . . . . . . . 128
5.2 Sorting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
5.2.1 Bubble Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
5.2.2 Selection Sort . . . . . . . . . . . . . . . . . . . . . . . . . 136
5.2.3 Insertion Sort . . . . . . . . . . . . . . . . . . . . . . . . . . 138
5.3 Working with Sorted Lists . . . . . . . . . . . . . . . . . . . . . . . 142
5.3.1 Maintaining a Sorted List . . . . . . . . . . . . . . . . . . . 142
5.3.2 Merging Sorted Lists . . . . . . . . . . . . . . . . . . . . . . 143
5.4 The Set ADT Revisited . . . . . . . . . . . . . . . . . . . . . . . . . 147
5.4.1 A Sorted List Implementation . . . . . . . . . . . . . . . . . 147
5.4.2 Comparing the Implementations . . . . . . . . . . . . . . . 152
Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
Programming Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153

Chapter 6: Linked Structures 155


6.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
6.2 The Singly Linked List . . . . . . . . . . . . . . . . . . . . . . . . . 159
6.2.1 Traversing the Nodes . . . . . . . . . . . . . . . . . . . . . 159
6.2.2 Searching for a Node . . . . . . . . . . . . . . . . . . . . . 161
6.2.3 Prepending Nodes . . . . . . . . . . . . . . . . . . . . . . . 162
6.2.4 Removing Nodes . . . . . . . . . . . . . . . . . . . . . . . . 163
6.3 The Bag ADT Revisited . . . . . . . . . . . . . . . . . . . . . . . . 165
6.3.1 A Linked List Implementation . . . . . . . . . . . . . . . . . 165
6.3.2 Comparing Implementations . . . . . . . . . . . . . . . . . 167
6.3.3 Linked List Iterators . . . . . . . . . . . . . . . . . . . . . . 168
6.4 More Ways to Build a Linked List . . . . . . . . . . . . . . . . . . . 169
6.4.1 Using a Tail Reference . . . . . . . . . . . . . . . . . . . . . 169
6.4.2 The Sorted Linked List . . . . . . . . . . . . . . . . . . . . . 171
6.5 The Sparse Matrix Revisited . . . . . . . . . . . . . . . . . . . . . 174
6.5.1 An Array of Linked Lists Implementation . . . . . . . . . . . 175
6.5.2 Comparing the Implementations . . . . . . . . . . . . . . . 178
6.6 Application: Polynomials . . . . . . . . . . . . . . . . . . . . . . . . 179
6.6.1 Polynomial Operations . . . . . . . . . . . . . . . . . . . . . 179
viii CONTENTS

6.6.2 The Polynomial ADT . . . . . . . . . . . . . . . . . . . . . . 181


6.6.3 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . 181
Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
Programming Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190

Chapter 7: Stacks 193


7.1 The Stack ADT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
7.2 Implementing the Stack . . . . . . . . . . . . . . . . . . . . . . . . 195
7.2.1 Using a Python List . . . . . . . . . . . . . . . . . . . . . . 195
7.2.2 Using a Linked List . . . . . . . . . . . . . . . . . . . . . . . 196
7.3 Stack Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
7.3.1 Balanced Delimiters . . . . . . . . . . . . . . . . . . . . . . 199
7.3.2 Evaluating Postfix Expressions . . . . . . . . . . . . . . . . 202
7.4 Application: Solving a Maze . . . . . . . . . . . . . . . . . . . . . . 206
7.4.1 Backtracking . . . . . . . . . . . . . . . . . . . . . . . . . . 207
7.4.2 Designing a Solution . . . . . . . . . . . . . . . . . . . . . . 208
7.4.3 The Maze ADT . . . . . . . . . . . . . . . . . . . . . . . . . 211
7.4.4 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . 214
Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
Programming Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219

Chapter 8: Queues 221


8.1 The Queue ADT . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
8.2 Implementing the Queue . . . . . . . . . . . . . . . . . . . . . . . . 222
8.2.1 Using a Python List . . . . . . . . . . . . . . . . . . . . . . 222
8.2.2 Using a Circular Array . . . . . . . . . . . . . . . . . . . . . 224
8.2.3 Using a Linked List . . . . . . . . . . . . . . . . . . . . . . . 228
8.3 Priority Queues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
8.3.1 The Priority Queue ADT . . . . . . . . . . . . . . . . . . . . 230
8.3.2 Implementation: Unbounded Priority Queue . . . . . . . . . 232
8.3.3 Implementation: Bounded Priority Queue . . . . . . . . . . 235
8.4 Application: Computer Simulations . . . . . . . . . . . . . . . . . . 237
8.4.1 Airline Ticket Counter . . . . . . . . . . . . . . . . . . . . . 237
8.4.2 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . 239
Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
Programming Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246

Chapter 9: Advanced Linked Lists 247


9.1 The Doubly Linked List . . . . . . . . . . . . . . . . . . . . . . . . . 247
9.1.1 Organization . . . . . . . . . . . . . . . . . . . . . . . . . . 247
9.1.2 List Operations . . . . . . . . . . . . . . . . . . . . . . . . . 248
9.2 The Circular Linked List . . . . . . . . . . . . . . . . . . . . . . . . 253
CONTENTS ix

9.2.1 Organization . . . . . . . . . . . . . . . . . . . . . . . . . . 253


9.2.2 List Operations . . . . . . . . . . . . . . . . . . . . . . . . . 254
9.3 Multi-Linked Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
9.3.1 Multiple Chains . . . . . . . . . . . . . . . . . . . . . . . . . 259
9.3.2 The Sparse Matrix . . . . . . . . . . . . . . . . . . . . . . . 260
9.4 Complex Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
9.5 Application: Text Editor . . . . . . . . . . . . . . . . . . . . . . . . . 263
9.5.1 Typical Editor Operations . . . . . . . . . . . . . . . . . . . 263
9.5.2 The Edit Buffer ADT . . . . . . . . . . . . . . . . . . . . . . 266
9.5.3 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . 268
Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
Programming Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275

Chapter 10: Recursion 277


10.1 Recursive Functions . . . . . . . . . . . . . . . . . . . . . . . . . . 277
10.2 Properties of Recursion . . . . . . . . . . . . . . . . . . . . . . . . 279
10.2.1 Factorials . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
10.2.2 Recursive Call Trees . . . . . . . . . . . . . . . . . . . . . . 281
10.2.3 The Fibonacci Sequence . . . . . . . . . . . . . . . . . . . 283
10.3 How Recursion Works . . . . . . . . . . . . . . . . . . . . . . . . . 283
10.3.1 The Run Time Stack . . . . . . . . . . . . . . . . . . . . . . 284
10.3.2 Using a Software Stack . . . . . . . . . . . . . . . . . . . . 286
10.3.3 Tail Recursion . . . . . . . . . . . . . . . . . . . . . . . . . 289
10.4 Recursive Applications . . . . . . . . . . . . . . . . . . . . . . . . . 290
10.4.1 Recursive Binary Search . . . . . . . . . . . . . . . . . . . 290
10.4.2 Towers of Hanoi . . . . . . . . . . . . . . . . . . . . . . . . 292
10.4.3 Exponential Operation . . . . . . . . . . . . . . . . . . . . . 296
10.4.4 Playing Tic-Tac-Toe . . . . . . . . . . . . . . . . . . . . . . 297
10.5 Application: The Eight-Queens Problem . . . . . . . . . . . . . . . 299
10.5.1 Solving for Four-Queens . . . . . . . . . . . . . . . . . . . . 301
10.5.2 Designing a Solution . . . . . . . . . . . . . . . . . . . . . . 303
Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
Programming Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308

Chapter 11: Hash Tables 309


11.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
11.2 Hashing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
11.2.1 Linear Probing . . . . . . . . . . . . . . . . . . . . . . . . . 312
11.2.2 Clustering . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
11.2.3 Rehashing . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
11.2.4 Efficiency Analysis . . . . . . . . . . . . . . . . . . . . . . . 320
11.3 Separate Chaining . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
x CONTENTS

11.4 Hash Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323


11.5 The HashMap Abstract Data Type . . . . . . . . . . . . . . . . . . 325
11.6 Application: Histograms . . . . . . . . . . . . . . . . . . . . . . . . 330
11.6.1 The Histogram Abstract Data Type . . . . . . . . . . . . . . 330
11.6.2 The Color Histogram . . . . . . . . . . . . . . . . . . . . . . 334
Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
Programming Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338

Chapter 12: Advanced Sorting 339


12.1 Merge Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
12.1.1 Algorithm Description . . . . . . . . . . . . . . . . . . . . . 340
12.1.2 Basic Implementation . . . . . . . . . . . . . . . . . . . . . 340
12.1.3 Improved Implementation . . . . . . . . . . . . . . . . . . . 342
12.1.4 Efficiency Analysis . . . . . . . . . . . . . . . . . . . . . . . 345
12.2 Quick Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
12.2.1 Algorithm Description . . . . . . . . . . . . . . . . . . . . . 348
12.2.2 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . 349
12.2.3 Efficiency Analysis . . . . . . . . . . . . . . . . . . . . . . . 353
12.3 How Fast Can We Sort? . . . . . . . . . . . . . . . . . . . . . . . . 353
12.4 Radix Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
12.4.1 Algorithm Description . . . . . . . . . . . . . . . . . . . . . 354
12.4.2 Basic Implementation . . . . . . . . . . . . . . . . . . . . . 356
12.4.3 Efficiency Analysis . . . . . . . . . . . . . . . . . . . . . . . 358
12.5 Sorting Linked Lists . . . . . . . . . . . . . . . . . . . . . . . . . . 358
12.5.1 Insertion Sort . . . . . . . . . . . . . . . . . . . . . . . . . . 359
12.5.2 Merge Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
Programming Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368

Chapter 13: Binary Trees 369


13.1 The Tree Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . 369
13.2 The Binary Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
13.2.1 Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
13.2.2 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . 375
13.2.3 Tree Traversals . . . . . . . . . . . . . . . . . . . . . . . . . 376
13.3 Expression Trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380
13.3.1 Expression Tree Abstract Data Type . . . . . . . . . . . . . 382
13.3.2 String Representation . . . . . . . . . . . . . . . . . . . . . 383
13.3.3 Tree Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . 384
13.3.4 Tree Construction . . . . . . . . . . . . . . . . . . . . . . . 386
13.4 Heaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
13.4.1 Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
CONTENTS xi

13.4.2 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . 395


13.4.3 The Priority Queue Revisited . . . . . . . . . . . . . . . . . 398
13.5 Heapsort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
13.5.1 Simple Implementation . . . . . . . . . . . . . . . . . . . . 400
13.5.2 Sorting In Place . . . . . . . . . . . . . . . . . . . . . . . . 400
13.6 Application: Morse Code . . . . . . . . . . . . . . . . . . . . . . . . 404
13.6.1 Decision Trees . . . . . . . . . . . . . . . . . . . . . . . . . 405
13.6.2 The ADT Definition . . . . . . . . . . . . . . . . . . . . . . . 406
Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407
Programming Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410

Chapter 14: Search Trees 411


14.1 The Binary Search Tree . . . . . . . . . . . . . . . . . . . . . . . . 412
14.1.1 Searching . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
14.1.2 Min and Max Values . . . . . . . . . . . . . . . . . . . . . . 415
14.1.3 Insertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
14.1.4 Deletions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420
14.1.5 Efficiency of Binary Search Trees . . . . . . . . . . . . . . . 425
14.2 Search Tree Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . 427
14.3 AVL Trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428
14.3.1 Insertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430
14.3.2 Deletions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
14.3.3 Implementation . . . . . . . . . . . . . . . . . . . . . . . . . 435
14.4 The 2-3 Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
14.4.1 Searching . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442
14.4.2 Insertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443
14.4.3 Efficiency of the 2-3 Tree . . . . . . . . . . . . . . . . . . . 449
Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
Programming Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452

Appendix A: Python Review 453


A.1 The Python Interpreter . . . . . . . . . . . . . . . . . . . . . . . . . 453
A.2 The Basics of Python . . . . . . . . . . . . . . . . . . . . . . . . . 454
A.2.1 Primitive Types . . . . . . . . . . . . . . . . . . . . . . . . . 455
A.2.2 Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . 456
A.2.3 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457
A.2.4 Arithmetic Operators . . . . . . . . . . . . . . . . . . . . . . 458
A.2.5 Logical Expressions . . . . . . . . . . . . . . . . . . . . . . 459
A.2.6 Using Functions and Methods . . . . . . . . . . . . . . . . 461
A.2.7 Standard Library . . . . . . . . . . . . . . . . . . . . . . . . 462
A.3 User Interaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463
A.3.1 Standard Input . . . . . . . . . . . . . . . . . . . . . . . . . 463
xii CONTENTS

A.3.2 Standard Output . . . . . . . . . . . . . . . . . . . . . . . . 464


A.4 Control Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
A.4.1 Selection Constructs . . . . . . . . . . . . . . . . . . . . . . 467
A.4.2 Repetition Constructs . . . . . . . . . . . . . . . . . . . . . 469
A.5 Collections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472
A.5.1 Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472
A.5.2 Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473
A.5.3 Tuples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 475
A.5.4 Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . 475
A.6 Text Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477
A.6.1 File Access . . . . . . . . . . . . . . . . . . . . . . . . . . . 477
A.6.2 Writing to Files . . . . . . . . . . . . . . . . . . . . . . . . . 478
A.6.3 Reading from Files . . . . . . . . . . . . . . . . . . . . . . . 479
A.7 User-Defined Functions . . . . . . . . . . . . . . . . . . . . . . . . 480
A.7.1 The Function Definition . . . . . . . . . . . . . . . . . . . . 480
A.7.2 Variable Scope . . . . . . . . . . . . . . . . . . . . . . . . . 483
A.7.3 Main Routine . . . . . . . . . . . . . . . . . . . . . . . . . . 483

Appendix B: User-Defined Modules 485


B.1 Structured Programs . . . . . . . . . . . . . . . . . . . . . . . . . . 485
B.2 Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486

Appendix C: Exceptions 489


C.1 Catching Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . 489
C.2 Raising Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . 490
C.3 Standard Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . 491
C.4 Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491

Appendix D: Classes 493


D.1 The Class Definition . . . . . . . . . . . . . . . . . . . . . . . . . . 493
D.1.1 Constructors . . . . . . . . . . . . . . . . . . . . . . . . . . 494
D.1.2 Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . 495
D.1.3 Using Modules . . . . . . . . . . . . . . . . . . . . . . . . . 497
D.1.4 Hiding Attributes . . . . . . . . . . . . . . . . . . . . . . . . 498
D.2 Overloading Operators . . . . . . . . . . . . . . . . . . . . . . . . . 500
D.3 Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
D.3.1 Deriving Child Classes . . . . . . . . . . . . . . . . . . . . . 503
D.3.2 Creating Class Instances . . . . . . . . . . . . . . . . . . . 504
D.3.3 Invoking Methods . . . . . . . . . . . . . . . . . . . . . . . 505
D.4 Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507
Preface

The standard second course in computer science has traditionally covered the fun-
damental data structures and algorithms, but more recently these topics have been
included in the broader topic of abstract data types. This book is no exception,
with the main focus on the design, use, and implementation of abstract data types.
The importance of designing and using abstract data types for easier modular pro-
gramming is emphasized throughout the text. The traditional data structures are
also presented throughout the text in terms of implementing the various abstract
data types. Multiple implementations using different data structures are used
throughout the text to reinforce the abstraction concept. Common algorithms are
also presented throughout the text as appropriate to provide complete coverage of
the typical data structures course.

Overview
The typical data structures course, which introduces a collection of fundamental
data structures and algorithms, can be taught using any of the different program-
ming languages available today. In recent years, more colleges have begun to adopt
the Python language for introducing students to programming and problem solv-
ing. Python provides several benefits over other languages such as C++ and Java,
the most important of which is that Python has a simple syntax that is easier to
learn. This book expands upon that use of Python by providing a Python-centric
text for the data structures course. The clean syntax and powerful features of the
language are used throughout, but the underlying mechanisms of these features
are fully explored not only to expose the “magic” but also to study their overall
efficiency.
For a number of years, many data structures textbooks have been written to
serve a dual role of introducing data structures and providing an in-depth study
of object-oriented programming (OOP). In some instances, this dual role may
compromise the original purpose of the data structures course by placing more focus
on OOP and less on the abstract data types and their underlying data structures.
To stress the importance of abstract data types, data structures, and algorithms, we
limit the discussion of OOP to the use of base classes for implementing the various
abstract data types. We do not use class inheritance or polymorphism in the main
part of the text but instead provide a basic introduction as an appendix. This
choice was made for several reasons. First, our objective is to provide a “back to

xiii
xiv PREFACE

basics” approach to learning data structures and algorithms without overwhelming


the reader with all of the OOP terminology and concepts, which is especially
important when the instructor has no plans to cover such topics. Second, different
instructors take different approaches with Python in their first course. Our aim is
to provide an excellent text to the widest possible audience. We do this by placing
the focus on the data structures and algorithms, while designing the examples to
allow the introduction of object-oriented programming if so desired.
The text also introduces the concept of algorithm analysis and explores the
efficiency of algorithms and data structures throughout the text. The major pre-
sentation of complexity analysis is contained in a single chapter, which allows it to
be omitted by instructors who do not normally cover such material in their data
structures course. Additional evaluations are provided throughout the text as new
algorithms and data structures are introduced, with the major details contained in
individual sections. When algorithm analysis is covered, examples of the various
complexity functions are introduced, including amortized cost. The latter is im-
portant when using Python since many of the list operations have a very efficient
amortized cost.

Prerequisites
This book assumes that the student has completed the standard introduction to
programming and problem-solving course using the Python language. Since the
contents of the first course can differ from college to college and instructor to
instructor, we assume the students are familiar with or can do the following:

ˆ Design and implement complete programs in Python, including the use of


modules and namespaces

ˆ Apply the basic data types and constructs, including loops, selection state-
ments, and subprograms (functions)

ˆ Create and use the built-in list and dictionary structures

ˆ Design and implement basics classes, including the use of helper methods and
private attributes

Contents and Organization


The text is organized into fourteen chapters and four appendices. The basic con-
cepts related to abstract data types, data structures, and algorithms are presented
in the first four chapters. Later chapters build on these earlier concepts to present
more advanced topics and introduce the student to additional abstract data types
and more advanced data structures. The book contains several topic threads that
run throughout the text, in which the topics are revisited in various chapters as
appropriate. The layout of the text does not force a rigid outline, but allows for the
PREFACE xv

reordering of some topics. For example, the chapters on recursion and hashing can
be presented at any time after the discussion of algorithm analysis in Chapter 4.

Chapter 1: Abstract Data Types. Introduces the concept of abstract data types
(ADTs) for both simple types, those containing individual data fields, and the more
complex types, those containing data structures. ADTs are presented in terms
of their definition, use, and implementation. After discussing the importance of
abstraction, we define several ADTs and then show how a well-defined ADT can
be used without knowing how its actually implemented. The focus then turns to
the implementation of the ADTs with an emphasis placed on the importance of
selecting an appropriate data structure. The chapter includes an introduction to
the Python iterator mechanism and provides an example of a user-defined iterator
for use with a container type ADT.

Chapter 2: Arrays. Introduces the student to the array structure, which is im-
portant since Python only provides the list structure and students are unlikely to
have seen the concept of the array as a fixed-sized structure in a first course using
Python. We define an ADT for a one-dimensional array and implement it using a
hardware array provided through a special mechanism of the C-implemented ver-
sion of Python. The two-dimensional array is also introduced and implemented
using a 1-D array of arrays. The array structures will be used throughout the text
in place of the Python’s list when it is the appropriate choice. The implementa-
tion of the list structure provided by Python is presented to show how the various
operations are implemented using a 1-D array. The Matrix ADT is introduced and
includes an implementation using a two-dimensional array that exposes the stu-
dents to an example of an ADT that is best implemented using a structure other
than the list or dictionary.

Chapter 3: Sets and Maps. This chapter reintroduces the students to both
the Set and Map (or dictionary) ADTs with which they are likely to be familiar
from their first programming course using Python. Even though Python provides
these ADTs, they both provide great examples of abstract data types that can be
implemented in many different ways. The chapter also continues the discussion of
arrays from the previous chapter by introducing multi-dimensional arrays (those
of two or more dimensions) along with the concept of physically storing these
using a one-dimensional array in either row-major or column-major order. The
chapter concludes with an example application that can benefit from the use of a
three-dimensional array.

Chapter 4: Algorithm Analysis. Introduces the basic concept and importance


of complexity analysis by evaluating the operations of Python’s list structure and
the Set ADT as implemented in the previous chapter. This information will be used
to provide a more efficient implementation of the Set ADT in the following chapter.
The chapter concludes by introducing the Sparse Matrix ADT and providing a more
efficient implementation with the use of a list in place of a two-dimensional array.
xvi PREFACE

Chapter 5: Searching and Sorting. Introduces the concepts of searching and


sorting and illustrates how the efficiency of some ADTs can be improved when
working with sorted sequences. Search operations for an unsorted sequence are
discussed and the binary search algorithm is introduced as a way of improving this
operation. Three of the basic sorting algorithms are also introduced to further
illustrate the use of algorithm analysis. A new implementation of the Set ADT is
provided to show how different data structures or data organizations can change
the efficiency of an ADT.

Chapter 6: Linked Structures. Provides an introduction to dynamic structures


by illustrating the construction and use of the singly linked list using dynamic
storage allocation. The common operations — traversal, searching, insertion, and
deletion — are presented as is the use of a tail reference when appropriate. Several
of the ADTs presented in earlier chapters are reimplemented using the singly linked
list, and the run times of their operations are compared to the earlier versions.
A new implementation of the Sparse Matrix is especially eye-opening to many
students as it uses an array of sorted linked lists instead of a single Python list as
was done in an earlier chapter.

Chapter 7: Stacks. Introduces the Stack ADT and includes implementations


using both a Python list and a linked list. Several common stack applications
are then presented, including balanced delimiter verification and the evaluation of
postfix expressions. The concept of backtracking is also introduced as part of the
application for solving a maze. A detailed discussion is provided in designing a
solution and a partial implementation.

Chapter 8: Queues. Introduces the Queue ADT and includes three different
implementations: Python list, circular array, and linked list. The priority queue
is introduced to provide an opportunity to discuss different structures and data
organization for an efficient implementation. The application of the queue presents
the concept of discrete event computer simulations using an airline ticket counter
as the example.

Chapter 9: Advanced Linked Lists. Continues the discussion of dynamic struc-


tures by introducing a collection of more advanced linked lists. These include the
doubly linked, circularly linked, and multi linked lists. The latter provides an
example of a linked structure containing multiple chains and is applied by reimple-
menting the Sparse Matrix to use two arrays of linked lists, one for the rows and
one for the columns. The doubly linked list is applied to the problem of designing
and implementing an Edit Buffer ADT for use with a basic text editor.

Chapter 10: Recursion. Introduces the use of recursion to solve various pro-
gramming problems. The properties of creating recursive functions are presented
along with common examples, including factorial, greatest common divisor, and
the Towers of Hanoi. The concept of backtracking is revisited to use recursion for
solving the eight-queens problem.
PREFACE xvii

Chapter 11: Hash Tables. Introduces the concept of hashing and the use of hash
tables for performing fast searches. Different addressing techniques are presented,
including those for both closed and open addressing. Collision resolution techniques
and hash function design are also discussed. The magic behind Python’s dictionary
structure, which uses a hash table, is exposed and its efficiency evaluated.

Chapter 12: Advanced Sorting. Continues the discussion of the sorting problem
by introducing the recursive sorting algorithms—merge sort and quick sort—along
with the radix distribution sort algorithm, all of which can be used to sort se-
quences. Some of the common techniques for sorting linked lists are also presented.

Chapter 13: Binary Trees. Presents the tree structure and the general binary
tree specifically. The construction and use of the binary tree is presented along
with various properties and the various traversal operations. The binary tree is
used to build and evaluate arithmetic expressions and in decoding Morse Code
sequences. The tree-based heap structure is also introduced along with its use in
implementing a priority queue and the heapsort algorithm.

Chapter 14: Search Trees. Continues the discussion from the previous chapter
by using the tree structure to solve the search problem. The basic binary search
tree and the balanced binary search tree (AVL) are both introduced along with
new implementations of the Map ADT. Finally, a brief introduction to the 2-3
multi-way tree is also provided, which shows an alternative to both the binary
search and AVL trees.

Appendix A: Python Review. Provides a review of the Python language and


concepts learned in the traditional first course. The review includes a presentation
of the basic constructs and built-in data structures.

Appendix B: User-Defined Modules. Describes the use of modules in creating


well structured programs. The different approaches for importing modules is also
discussed along with the use of namespaces.

Appendix C: Exceptions. Provides a basic introduction to the use of exceptions


for handling and raising errors during program execution.

Appendix D: Classes. Introduces the basic concepts of object-oriented program-


ming, including encapsulation, inheritance, and polymorphism. The presentation
is divided into two main parts. The first part presents the basic design and use
of classes for those instructors who use a “back to basics” approach in teaching
data structures. The second part briefly explores the more advanced features of
inheritance and polymorphism for those instructors who typically include these
topics in their course.
xviii PREFACE

Acknowledgments
There are a number of individuals I would like to thank for helping to make this
book possible. First, I must acknowledge two individuals who served as mentors
in the early part of my career. Mary Dayne Gregg (University of Southern Mis-
sissippi), who was the best computer science teacher I have ever known, shared
her love of teaching and provided a great role model in academia. Richard Prosl
(Professor Emeritus, College of William and Mary) served not only as my graduate
advisor but also shared great insight into teaching and helped me to become a good
teacher.
A special thanks to the many students I have taught over the years, especially
those at Washington and Lee University, who during the past five years used draft
versions of the manuscript and provided helpful suggestions. I would also like to
thank some of my colleagues who provided great advice and the encouragement
to complete the project: Sara Sprenkle (Washington and Lee University), Debbie
Noonan (College of William and Mary), and Robert Noonan (College of William
and Mary).
I am also grateful to the following individuals who served as outside review-
ers and provided valuable feedback and helpful suggestions: Esmail Bonakdarian
(Franklin University), David Dubin (University of Illinois at Urbana-Champaign)
Mark E. Fenner (Norwich University), Robert Franks (Central College), Charles J.
Leska (Randolph-Macon College), Fernando Martincic (Wayne State University),
Joseph D. Sloan (Wofford College), David A. Sykes (Wofford College), and Stan
Thomas (Wake Forest University).
Finally, I would like to thank everyone at John Wiley & Sons who helped make
this book possible. I would especially like to thank Beth Golub, Mike Berlin, and
Amy Weintraub, with whom I worked closely throughout the process and who
helped to make this first book an enjoyable experience.

Rance D. Necaise
CHAPTER 1
Abstract Data Types

The foundation of computer science is based on the study of algorithms. An al-


gorithm is a sequence of clear and precise step-by-step instructions for solving a
problem in a finite amount of time. Algorithms are implemented by translating
the step-by-step instructions into a computer program that can be executed by
a computer. This translation process is called computer programming or sim-
ply programming . Computer programs are constructed using a programming
language appropriate to the problem. While programming is an important part
of computer science, computer science is not the study of programming. Nor is
it about learning a particular programming language. Instead, programming and
programming languages are tools used by computer scientists to solve problems.

1.1 Introduction
Data items are represented within a computer as a sequence of binary digits. These
sequences can appear very similar but have different meanings since computers
can store and manipulate different types of data. For example, the binary se-
quence 01001100110010110101110011011100 could be a string of characters, an in-
teger value, or a real value. To distinguish between the different types of data, the
term type is often used to refer to a collection of values and the term data type to
refer to a given type along with a collection of operations for manipulating values
of the given type.
Programming languages commonly provide data types as part of the language
itself. These data types, known as primitives, come in two categories: simple
and complex. The simple data types consist of values that are in the most
basic form and cannot be decomposed into smaller parts. Integer and real types,
for example, consist of single numeric values. The complex data types, on the
other hand, are constructed of multiple components consisting of simple types or
other complex types. In Python, objects, strings, lists, and dictionaries, which can

1
2 CHAPTER 1 Abstract Data Types

contain multiple values, are all examples of complex types. The primitive types
provided by a language may not be sufficient for solving large complex problems.
Thus, most languages allow for the construction of additional data types, known
as user-defined types since they are defined by the programmer and not the
language. Some of these data types can themselves be very complex.

1.1.1 Abstractions
To help manage complex problems and complex data types, computer scientists
typically work with abstractions. An abstraction is a mechanism for separat-
ing the properties of an object and restricting the focus to those relevant in the
current context. The user of the abstraction does not have to understand all of
the details in order to utilize the object, but only those relevant to the current task
or problem.
Two common types of abstractions encountered in computer science are proce-
dural, or functional, abstraction and data abstraction. Procedural abstraction
is the use of a function or method knowing what it does but ignoring how it’s
accomplished. Consider the mathematical square root function which you have
probably used at some point. You know the function will compute the square root
of a given number, but do you know how the square root is computed? Does it
matter if you know how it is computed, or is simply knowing how to correctly use
the function sufficient? Data abstraction is the separation of the properties of a
data type (its values and operations) from the implementation of that data type.
You have used strings in Python many times. But do you know how they are
implemented? That is, do you know how the data is structured internally or how
the various operations are implemented?
Typically, abstractions of complex problems occur in layers, with each higher
layer adding more abstraction than the previous. Consider the problem of repre-
senting integer values on computers and performing arithmetic operations on those
values. Figure 1.1 illustrates the common levels of abstractions used with integer
arithmetic. At the lowest level is the hardware with little to no abstraction since it
includes binary representations of the values and logic circuits for performing the
arithmetic. Hardware designers would deal with integer arithmetic at this level
and be concerned with its correct implementation. A higher level of abstraction
for integer values and arithmetic is provided through assembly language, which in-
volves working with binary values and individual instructions corresponding to the
underlying hardware. Compiler writers and assembly language programmers would
work with integer arithmetic at this level and must ensure the proper selection of
assembly language instructions to compute a given mathematical expression. For
example, suppose we wish to compute x = a + b − 5. At the assembly language
level, this expression must be split into multiple instructions for loading the values
from memory, storing them into registers, and then performing each arithmetic
operation separately, as shown in the following psuedocode:

loadFromMem( R1, 'a' )


loadFromMem( R2, 'b' )
1.1 Introduction 3

add R0, R1, R2


sub R0, R0, 5
storeToMem( R0, 'x' )

To avoid this level of complexity, high-level programming languages add an-


other layer of abstraction above the assembly language level. This abstraction
is provided through a primitive data type for storing integer values and a set of
well-defined operations that can be performed on those values. By providing this
level of abstraction, programmers can work with variables storing decimal values
and specify mathematical expressions in a more familiar notation (x = a + b − 5)
than is possible with assembly language instructions. Thus, a programmer does
not need to know the assembly language instructions required to evaluate a math-
ematical expression or understand the hardware implementation in order to use
integer arithmetic in a computer program.

Software-Implemented
Software-Implemented Higher Level
Big
Big Integers
Integers

High-Level
High-Level Language
Language
Instructions
Instructions

Assembly
Assembly Language
Language
Instructions
Instructions

Hardware
Hardware Lower Level
Implementation
Implementation

Figure 1.1: Levels of abstraction used with integer arithmetic.

One problem with the integer arithmetic provided by most high-level languages
and in computer hardware is that it works with values of a limited size. On 32-bit
architecture computers, for example, signed integer values are limited to the range
−231 . . . (231 − 1). What if we need larger values? In this case, we can provide
long or “big integers” implemented in software to allow values of unlimited size.
This would involve storing the individual digits and implementing functions or
methods for performing the various arithmetic operations. The implementation
of the operations would use the primitive data types and instructions provided by
the high-level language. Software libraries that provide big integer implementations
are available for most common programming languages. Python, however, actually
provides software-implemented big integers as part of the language itself.

1.1.2 Abstract Data Types


An abstract data type (or ADT ) is a programmer-defined data type that spec-
ifies a set of data values and a collection of well-defined operations that can be
performed on those values. Abstract data types are defined independent of their
4 CHAPTER 1 Abstract Data Types

implementation, allowing us to focus on the use of the new data type instead of
how it’s implemented. This separation is typically enforced by requiring interac-
tion with the abstract data type through an interface or defined set of operations.
This is known as information hiding . By hiding the implementation details and
requiring ADTs to be accessed through an interface, we can work with an ab-
straction and focus on what functionality the ADT provides instead of how that
functionality is implemented.
Abstract data types can be viewed like black boxes as illustrated in Figure 1.2.
User programs interact with instances of the ADT by invoking one of the several
operations defined by its interface. The set of operations can be grouped into four
categories:

ˆ Constructors: Create and initialize new instances of the ADT.


ˆ Accessors: Return data contained in an instance without modifying it.
ˆ Mutators: Modify the contents of an ADT instance.
ˆ Iterators: Process individual data components sequentially.

User programs interact with


ADTs through their interface
or set of operations. string ADT
str() The implementation
User
User details are hidden
Program
Program upper() as if inside a black box.
lower()
:

Figure 1.2: Separating the ADT definition from its implementation.

The implementation of the various operations are hidden inside the black box,
the contents of which we do not have to know in order to utilize the ADT. There
are several advantages of working with abstract data types and focusing on the
“what” instead of the “how.”

ˆ We can focus on solving the problem at hand instead of getting bogged down
in the implementation details. For example, suppose we need to extract a
collection of values from a file on disk and store them for later use in our
program. If we focus on the implementation details, then we have to worry
about what type of storage structure to use, how it should be used, and
whether it is the most efficient choice.

ˆ We can reduce logical errors that can occur from accidental misuse of storage
structures and data types by preventing direct access to the implementation. If
we used a list to store the collection of values in the previous example, there
is the opportunity to accidentally modify its contents in a part of our code
1.1 Introduction 5

where it was not intended. This type of logical error can be difficult to track
down. By using ADTs and requiring access via the interface, we have fewer
access points to debug.

ˆ The implementation of the abstract data type can be changed without having
to modify the program code that uses the ADT. There are many times when
we discover the initial implementation of an ADT is not the most efficient or
we need the data organized in a different way. Suppose our initial approach
to the previous problem of storing a collection of values is to simply append
new values to the end of the list. What happens if we later decide the items
should be arranged in a different order than simply appending them to the
end? If we are accessing the list directly, then we will have to modify our code
at every point where values are added and make sure they are not rearranged
in other places. By requiring access via the interface, we can easily “swap out”
the black box with a new implementation with no impact on code segments
that use the ADT.

ˆ It’s easier to manage and divide larger programs into smaller modules, al-
lowing different members of a team to work on the separate modules. Large
programming projects are commonly developed by teams of programmers in
which the workload is divided among the members. By working with ADTs
and agreeing on their definition, the team can better ensure the individual
modules will work together when all the pieces are combined. Using our pre-
vious example, if each member of the team directly accessed the list storing
the collection of values, they may inadvertently organize the data in different
ways or modify the list in some unexpected way. When the various modules
are combined, the results may be unpredictable.

1.1.3 Data Structures


Working with abstract data types, which separate the definition from the imple-
mentation, is advantageous in solving problems and writing programs. At some
point, however, we must provide a concrete implementation in order for the pro-
gram to execute. ADTs provided in language libraries, like Python, are imple-
mented by the maintainers of the library. When you define and create your own
abstract data types, you must eventually provide an implementation. The choices
you make in implementing your ADT can affect its functionality and efficiency.
Abstract data types can be simple or complex. A simple ADT is composed
of a single or several individually named data fields such as those used to represent
a date or rational number. The complex ADTs are composed of a collection of
data values such as the Python list or dictionary. Complex abstract data types
are implemented using a particular data structure, which is the physical rep-
resentation of how data is organized and manipulated. Data structures can be
characterized by how they store and organize the individual data elements and
what operations are available for accessing and manipulating the data.
6 CHAPTER 1 Abstract Data Types

There are many common data structures, including arrays, linked lists, stacks,
queues, and trees, to name a few. All data structures store a collection of values,
but differ in how they organize the individual data items and by what operations
can be applied to manage the collection. The choice of a particular data structure
depends on the ADT and the problem at hand. Some data structures are better
suited to particular problems. For example, the queue structure is perfect for
implementing a printer queue, while the B-Tree is the better choice for a database
index. No matter which data structure we use to implement an ADT, by keeping
the implementation separate from the definition, we can use an abstract data type
within our program and later change to a different implementation, as needed,
without having to modify our existing code.

1.1.4 General Definitions


There are many different terms used in computer science. Some of these can have
different meanings among the various textbooks and programming languages. To
aide the reader and to avoid confusion, we define some of the common terms we
will be using throughout the text.
A collection is a group of values with no implied organization or relationship
between the individual values. Sometimes we may restrict the elements to a specific
data type such as a collection of integers or floating-point values.
A container is any data structure or abstract data type that stores and orga-
nizes a collection. The individual values of the collection are known as elements
of the container and a container with no elements is said to be empty . The orga-
nization or arrangement of the elements can vary from one container to the next as
can the operations available for accessing the elements. Python provides a number
of built-in containers, which include strings, tuples, lists, dictionaries, and sets.
A sequence is a container in which the elements are arranged in linear order
from front to back, with each element accessible by position. Throughout the text,
we assume that access to the individual elements based on their position within
the linear order is provided using the subscript operator. Python provides two
immutable sequences, strings and tuples, and one mutable sequence, the list. In
the next chapter, we introduce the array structure, which is also a commonly used
mutable sequence.
A sorted sequence is one in which the position of the elements is based on
a prescribed relationship between each element and its successor. For example,
we can create a sorted sequence of integers in which the elements are arranged in
ascending or increasing order from smallest to largest value.
In computer science, the term list is commonly used to refer to any collection
with a linear ordering. The ordering is such that every element in the collection,
except the first one, has a unique predecessor and every element, except the last
one, has a unique successor. By this definition, a sequence is a list, but a list is
not necessarily a sequence since there is no requirement that a list provide access
to the elements by position. Python, unfortunately, uses the same name for its
built-in mutable sequence type, which in other languages would be called an array
1.2 The Date Abstract Data Type 7

list or vector abstract data type. To avoid confusion, we will use the term list to
refer to the data type provided by Python and use the terms general list or list
structure when referring to the more general list structure as defined earlier.

1.2 The Date Abstract Data Type


An abstract data type is defined by specifying the domain of the data elements
that compose the ADT and the set of operations that can be performed on that
domain. The definition should provide a clear description of the ADT including
both its domain and each of its operations as only those operations specified can be
performed on an instance of the ADT. Next, we provide the definition of a simple
abstract data type for representing a date in the proleptic Gregorian calendar.

1.2.1 Defining the ADT


The Gregorian calendar was introduced in the year 1582 by Pope Gregory XIII to
replace the Julian calendar. The new calendar corrected for the miscalculation of
the lunar year and introduced the leap year. The official first date of the Gregorian
calendar is Friday, October 15, 1582. The proleptic Gregorian calendar is an
extension for accommodating earlier dates with the first date on November 24,
4713 BC. This extension simplifies the handling of dates across older calendars
and its use can be found in many software applications.

Define Date ADT

A date represents a single day in the proleptic Gregorian calendar in which the
first day starts on November 24, 4713 BC.
 Date( month, day, year ): Creates a new Date instance initialized to the
given Gregorian date which must be valid. Year 1 BC and earlier are indicated
by negative year components.
 day(): Returns the Gregorian day number of this date.
 month(): Returns the Gregorian month number of this date.
 year(): Returns the Gregorian year of this date.
 monthName(): Returns the Gregorian month name of this date.
 dayOfWeek(): Returns the day of the week as a number between 0 and 6 with
0 representing Monday and 6 representing Sunday.
 numDays( otherDate ): Returns the number of days as a positive integer be-
tween this date and the otherDate.
 isLeapYear(): Determines if this date falls in a leap year and returns the
appropriate boolean value.
8 CHAPTER 1 Abstract Data Types

 advanceBy( days ): Advances the date by the given number of days. The date
is incremented if days is positive and decremented if days is negative. The
date is capped to November 24, 4714 BC, if necessary.
 comparable ( otherDate ): Compares this date to the otherDate to deter-
mine their logical ordering. This comparison can be done using any of the
logical operators <, <=, >, >=, ==, !=.
 toString (): Returns a string representing the Gregorian date in the format
mm/dd/yyyy. Implemented as the Python operator that is automatically called
via the str() constructor.

The abstract data types defined in the text will be implemented as Python
classes. When defining an ADT, we specify the ADT operations as method pro-
totypes. The class constructor, which is used to create an instance of the ADT, is
indicated by the name of the class used in the implementation.
Python allows classes to define or overload various operators that can be used
more naturally in a program without having to call a method by name. We define
all ADT operations as named methods, but implement some of them as operators
when appropriate instead of using the named method. The ADT operations that
will be implemented as Python operators are indicated in italicized text and a brief
comment is provided in the ADT definition indicating the corresponding operator.
This approach allows us to focus on the general ADT specification that can be
easily translated to other languages if the need arises but also allows us to take
advantage of Python’s simple syntax in various sample programs.

1.2.2 Using the ADT


To illustrate the use of the Date ADT, consider the program in Listing 1.1, which
processes a collection of birth dates. The dates are extracted from standard input
and examined. Those dates that indicate the individual is at least 21 years of age
based on a target date are printed to standard output. The user is continuously
prompted to enter a birth date until zero is entered for the month.
This simple example illustrates an advantage of working with an abstraction
by focusing on what functionality the ADT provides instead of how that function-
ality is implemented. By hiding the implementation details, we can use an ADT
independent of its implementation. In fact, the choice of implementation for the
Date ADT will have no effect on the instructions in our example program.

Class Definitions. Classes are the foundation of object-oriented


i
NOTE

programing languages and they provide a convenient mechanism for


defining and implementing abstract data types. A review of Python classes
is provided in Appendix D.
1.2 The Date Abstract Data Type 9

Listing 1.1 The checkdates.py program.

1 # Extracts a collection of birth dates from the user and determines


2 # if each individual is at least 21 years of age.
3 from date import Date
4
5 def main():
6 # Date before which a person must have been born to be 21 or older.
7 bornBefore = Date(6, 1, 1988)
8
9 # Extract birth dates from the user and determine if 21 or older.
10 date = promptAndExtractDate()
11 while date is not None :
12 if date <= bornBefore :
13 print( "Is at least 21 years of age: ", date )
14 date = promptAndExtractDate()
15
16 # Prompts for and extracts the Gregorian date components. Returns a
17 # Date object or None when the user has finished entering dates.
18 def promptAndExtractDate():
19 print( "Enter a birth date." )
20 month = int( input("month (0 to quit): ") )
21 if month == 0 :
22 return None
23 else :
24 day = int( input("day: ") )
25 year = int( input("year: ") )
26 return Date( month, day, year )
27
28 # Call the main routine.
29 main()

1.2.3 Preconditions and Postconditions


In defining the operations, we must include a specification of required inputs and
the resulting output, if any. In addition, we must specify the preconditions and
postconditions for each operation. A precondition indicates the condition or
state of the ADT instance and inputs before the operation can be performed. A
postcondition indicates the result or ending state of the ADT instance after the
operation is performed. The precondition is assumed to be true while the postcon-
dition is a guarantee as long as the preconditions are met. Attempting to perform
an operation in which the precondition is not satisfied should be flagged as an er-
ror. Consider the use of the pop(i) method for removing a value from a list. When
this method is called, the precondition states the supplied index must be within
the legal range. Upon successful completion of the operation, the postcondition
guarantees the item has been removed from the list. If an invalid index, one that
is out of the legal range, is passed to the pop() method, an exception is raised.
All operations have at least one precondition, which is that the ADT instance
has to have been previously initialized. In an object-oriented language, this pre-
condition is automatically verified since an object must be created and initialized
10 CHAPTER 1 Abstract Data Types

via the constructor before any operation can be used. Other than the initialization
requirement, an operation may not have any other preconditions. It all depends
on the type of ADT and the respective operation. Likewise, some operations may
not have a postcondition, as is the case for simple access methods, which simply
return a value without modifying the ADT instance itself. Throughout the text,
we do not explicitly state the precondition and postcondition as such, but they are
easily identified from the description of the ADT operations.
When implementing abstract data types, it’s important that we ensure the
proper execution of the various operations by verifying any stated preconditions.
The appropriate mechanism when testing preconditions for abstract data types is
to test the precondition and raise an exception when the precondition fails. You
then allow the user of the ADT to decide how they wish to handle the error, either
catch it or allow the program to abort.
Python, like many other object-oriented programming languages, raises an ex-
ception when an error occurs. An exception is an event that can be triggered
and optionally handled during program execution. When an exception is raised
indicating an error, the program can contain code to catch and gracefully handle
the exception; otherwise, the program will abort. Python also provides the assert
statement, which can be used to raise an AssertionError exception. The assert
statement is used to state what we assume to be true at a given point in the pro-
gram. If the assertion fails, Python automatically raises an AssertionError and
aborts the program, unless the exception is caught.
Throughout the text, we use the assert statement to test the preconditions
when implementing abstract data types. This allows us to focus on the implemen-
tation of the ADTs instead of having to spend time selecting the proper exception
to raise or creating new exceptions for use with our ADTs. For more information
on exceptions and assertions, refer to Appendix C.

1.2.4 Implementing the ADT


After defining the ADT, we need to provide an implementation in an appropriate
language. In our case, we will always use Python and class definitions, but any
programming language could be used. A partial implementation of the Date class is
provided in Listing 1.2, with the implementation of some methods left as exercises.

Date Representations
There are two common approaches to storing a date in an object. One approach
stores the three components—month, day, and year—as three separate fields. With
this format, it is easy to access the individual components, but it’s difficult to
compare two dates or to compute the number of days between two dates since the
number of days in a month varies from month to month. The second approach
stores the date as an integer value representing the Julian day, which is the number
of days elapsed since the initial date of November 24, 4713 BC (using the Gregorian
calendar notation). Given a Julian day number, we can compute any of the three
Gregorian components and simply subtract the two integer values to determine
1.2 The Date Abstract Data Type 11

which occurs first or how many days separate the two dates. We are going to use
the latter approach as it is very common for storing dates in computer applications
and provides for an easy implementation.

Listing 1.2 Partial implementation of the date.py module.

1 # Implements a proleptic Gregorian calendar date as a Julian day number.


2
3 class Date :
4 # Creates an object instance for the specified Gregorian date.
5 def __init__( self, month, day, year ):
6 self._julianDay = 0
7 assert self._isValidGregorian( month, day, year ), \
8 "Invalid Gregorian date."
9
10 # The first line of the equation, T = (M - 14) / 12, has to be changed
11 # since Python's implementation of integer division is not the same
12 # as the mathematical definition.
13 tmp = 0
14 if month < 3 :
15 tmp = -1
16 self._julianDay = day - 32075 + \
17 (1461 * (year + 4800 + tmp) // 4) + \
18 (367 * (month - 2 - tmp * 12) // 12) - \
19 (3 * ((year + 4900 + tmp) // 100) // 4)
20
21 # Extracts the appropriate Gregorian date component.
22 def month( self ):
23 return (self._toGregorian())[0] # returning M from (M, d, y)
24
25 def day( self ):
26 return (self._toGregorian())[1] # returning D from (m, D, y)
27
28 def year( self ):
29 return (self._toGregorian())[2] # returning Y from (m, d, Y)
30
31 # Returns day of the week as an int between 0 (Mon) and 6 (Sun).
32 def dayOfWeek( self ):
33 month, day, year = self._toGregorian()
34 if month < 3 :
35 month = month + 12
36 year = year - 1
37 return ((13 * month + 3) // 5 + day + \
38 year + year // 4 - year // 100 + year // 400) % 7
39
40 # Returns the date as a string in Gregorian format.
41 def __str__( self ):
42 month, day, year = self._toGregorian()
43 return "%02d/%02d/%04d" % (month, day, year)
44
45 # Logically compares the two dates.
46 def __eq__( self, otherDate ):
47 return self._julianDay == otherDate._julianDay
48
(Listing Continued)
12 CHAPTER 1 Abstract Data Types

Listing 1.2 Continued . . .


49 def __lt__( self, otherDate ):
50 return self._julianDay < otherDate._julianDay
51
52 def __le__( self, otherDate ):
53 return self._julianDay <= otherDate._julianDay
54
55 # The remaining methods are to be included at this point.
56 # ......
57
58 # Returns the Gregorian date as a tuple: (month, day, year).
59 def _toGregorian( self ):
60 A = self._julianDay + 68569
61 B = 4 * A // 146097
62 A = A - (146097 * B + 3) // 4
63 year = 4000 * (A + 1) // 1461001
64 A = A - (1461 * year // 4) + 31
65 month = 80 * A // 2447
66 day = A - (2447 * month // 80)
67 A = month // 11
68 month = month + 2 - (12 * A)
69 year = 100 * (B - 49) + year + A
70 return month, day, year

Constructing the Date


We begin our discussion of the implementation with the constructor, which is shown
in lines 5–19 of Listing 1.2. The Date ADT will need only a single attribute to store
the Julian day representing the given Gregorian date. To convert a Gregorian date
to a Julian day number, we use the following formula1 where day 0 corresponds to
November 24, 4713 BC and all operations involve integer arithmetic.

T = (M - 14) / 12
jday = D - 32075 + (1461 * (Y + 4800 + T) / 4) +
(367 * (M - 2 - T * 12) / 12) -
(3 * ((Y + 4900 + T) / 100) / 4)

Before attempting to convert the Gregorian date to a Julian day, we need


to verify it’s a valid date. This is necessary since the precondition states the
supplied Gregorian date must be valid. The isValidGregorian() helper method
is used to verify the validity of the given Gregorian date. This helper method,
the implementation of which is left as an exercise, tests the supplied Gregorian
date components and returns the appropriate boolean value. If a valid date is
supplied to the constructor, it is converted to the equivalent Julian day using the
equation provided earlier. Note the statements in lines 13–15. The equation for
converting a Gregorian date to a Julian day number uses integer arithmetic, but
1
Seidelmann, P. Kenneth (ed.) (1992). Explanatory Supplement to the Astronomical Almanac,
Chapter 12, pp. 604—606, University Science Books.
1.2 The Date Abstract Data Type 13

Comments. Class definitions and methods should be properly com-


i mented to aide the user in knowing what the class and/or methods do.

NOTE
To conserve space, however, classes and methods presented in this book
do not routinely include these comments since the surrounding text provides
a full explanation.

the equation line T = (M - 14) / 12 produces an incorrect result in Python due to


its implementation of integer division, which is not the same as the mathematical
definition. By definition, the result of the integer division -11/12 is 0, but Python
computes this as b−11/12.0c resulting in -1. Thus, we had to modify the first line
of the equation to produce the correct Julian day when the month component is
greater than 2.

Protected Attributes and Methods. Python does not provide a tech-


!
CAUTION

nique to protect attributes and helper methods in order to prevent their


use outside the class definition. In this text, we use identifier names, which
begin with a single underscore to flag those attributes and methods that
should be considered protected and rely on the user of the class to not at-
tempt a direct access.

The Gregorian Date


To access the Gregorian date components the Julian day must be converted back
to Gregorian. This conversion is needed in several of the ADT operations. Instead
of duplicating the formula each time it’s needed, we create a helper method to
handle the conversion as illustrated in lines 59–70 of Listing 1.2.
The toGregorian() method returns a tuple containing the day, month, and
year components. As with the conversion from Gregorian to Julian, integer arith-
metic operations are used throughout the conversion formula. By returning a tuple,
we can call the helper method and use the appropriate component from the tuple
for the given Gregorian component access method, as illustrated in lines 22–29.
The dayOfWeek() method, shown in lines 32–38, also uses the toGregorian()
conversion helper method. We determine the day of the week based on the Gre-
gorian components using a simple formula that returns an integer value between 0
and 6, where 0 represents Monday, 1 represents Tuesday, and so on.
The toString operation defined by the ADT is implemented in lines 41–43 by
overloading Python’s str method. It creates a string representation of a date in
Gregorian format. This can be done using the string format operator and supplying
the values returned from the conversion helper method. By using Python’s str
method, Python automatically calls this method on the object when you attempt
to print or convert an object to a string as in the following example:
14 CHAPTER 1 Abstract Data Types

firstDay = Date( 9, 1, 2006 )


print( firstDay )

Comparing Date Objects


We can logically compare two Date instances to determine their calendar order.
When using a Julian day to represent the dates, the date comparison is as simple
as comparing the two integer values and returning the appropriate boolean value
based on the result of that comparison. The “comparable” ADT operation is
implemented using Python’s logical comparison operators as shown in lines 46–53
of Listing 1.2. By implementing the methods for the logical comparison operators,
instances of the class become comparable objects. That is, the objects can be
compared against each other to produce a logical ordering.
You will notice that we implemented only three of the logical comparison op-
erators. The reason for this is that starting with Python version 3, Python will
automatically swap the operands and call the appropriate reflective method when
necessary. For example, if we use the expression a > b with Date objects in our
program, Python will automatically swap the operands and call b < a instead since
the lt method is defined but not gt . It will do the same for a >= b and
a <= b. When testing for equality, Python will automatically invert the result
when only one of the equality operators (== or !=) is defined. Thus, we need only
define one operator from each of the following pairs to achieve the full range of
logical comparisons: < or >, <= or >=, and == or !=. For more information on
overloading operators, refer to Appendix D.

Overloading Operators. User-defined classes can implement meth-


ods to define many of the standard Python operators such as +, *, %,
and ==, as well as the standard named operators such as in and not in.
TIP

This allows for a more natural use of the objects instead of having to call
specific named methods. It can be tempting to define operators for every
class you create, but you should limit the definition of operator methods for
classes where the specific operator has a meaningful purpose.

1.3 Bags
The Date ADT provided an example of a simple abstract data type. To illustrate
the design and implementation of a complex abstract data type, we define the Bag
ADT. A bag is a simple container like a shopping bag that can be used to store a
collection of items. The bag container restricts access to the individual items by
only defining operations for adding and removing individual items, for determining
if an item is in the bag, and for traversing over the collection of items.
1.3 Bags 15

1.3.1 The Bag Abstract Data Type


There are several variations of the Bag ADT with the one described here being a
simple bag. A grab bag is similar to the simple bag but the items are removed
from the bag at random. Another common variation is the counting bag, which
includes an operation that returns the number of occurrences in the bag of a given
item. Implementations of the grab bag and counting bag are left as exercises.

Define Bag ADT

A bag is a container that stores a collection in which duplicate values are allowed.
The items, each of which is individually stored, have no particular order but they
must be comparable.
 Bag(): Creates a bag that is initially empty.

 length (): Returns the number of items stored in the bag. Accessed using
the len() function.

 contains ( item ): Determines if the given target item is stored in the bag
and returns the appropriate boolean value. Accessed using the in operator.

 add( item ): Adds the given item to the bag.

 remove( item ): Removes and returns an occurrence of item from the bag.
An exception is raised if the element is not in the bag.

 iterator (): Creates and returns an iterator that can be used to iterate over
the collection of items.

You may have noticed our definition of the Bag ADT does not include an
operation to convert the container to a string. We could include such an operation,
but creating a string for a large collection is time consuming and requires a large
amount of memory. Such an operation can be beneficial when debugging a program
that uses an instance of the Bag ADT. Thus, it’s not uncommon to include the
str operator method for debugging purposes, but it would not typically be used
in production software. We will usually omit the inclusion of a str operator
method in the definition of our abstract data types, except in those cases where it’s
meaningful, but you may want to include one temporarily for debugging purposes.

Examples
Given the abstract definition of the Bag ADT, we can create and use a bag without
knowing how it is actually implemented. Consider the following simple example,
which creates a bag and asks the user to guess one of the values it contains.
16 CHAPTER 1 Abstract Data Types

myBag = Bag()
myBag.add( 19 )
myBag.add( 74 )
myBag.add( 23 )
myBag.add( 19 )
myBag.add( 12 )

value = int( input("Guess a value contained in the bag.") )


if value in myBag:
print( "The bag contains the value", value )
else :
print( "The bag does not contain the value", value )

Next, consider the checkdates.py sample program from the previous section
where we extracted birth dates from the user and determined which ones were
for individuals who were at least 21 years of age. Suppose we want to keep the
collection of birth dates for later use. It wouldn’t make sense to require the user to
re-enter the dates multiple times. Instead, we can store the birth dates in a bag as
they are entered and access them later, as many times as needed. The Bag ADT
is a perfect container for storing objects when the position or order of a specific
item does not matter. The following is a new version of the main routine for our
birth date checking program from Listing 1.1:

#pgm: checkdates2.py (modified main() from checkdates.py)


from linearbag import Bag
from date import Date

def main():
bornBefore = Date( 6, 1, 1988 )
bag = Bag()

# Extract dates from the user and place them in the bag.
date = promptAndExtractDate()
while date is not None :
bag.add( date )
date = promptAndExtractDate()

# Iterate over the bag and check the age.


for date in bag :
if date <= bornBefore :
print( "Is at least 21 years of age: ", date )

Why a Bag ADT?


You may be wondering, why do we need the Bag ADT when we could simply
use the list to store the items? For a small program and a small collection of
data, using a list would be appropriate. When working with large programs and
multiple team members, however, abstract data types provide several advantages
as described earlier in Section 1.1.2. By working with the abstraction of a bag,
we can: a) focus on solving the problem at hand instead of worrying about the
1.3 Bags 17

implementation of the container, b) reduce the chance of introducing errors from


misuse of the list since it provides additional operations that are not appropriate
for a bag, c) provide better coordination between different modules and designers,
and d) easily swap out our current implementation of the Bag ADT for a different,
possibly more efficient, version later.

1.3.2 Selecting a Data Structure


The implementation of a complex abstract data type typically requires the use of
a data structure for organizing and managing the collection of data items. There
are many different structures from which to choose. So how do we know which to
use? We have to evaluate the suitability of a data structure for implementing a
given abstract data type, which we base on the following criteria:

1. Does the data structure provide for the storage requirements as specified by
the domain of the ADT? Abstract data types are defined to work with a
specific domain of data values. The data structure we choose must be capable
of storing all possible values in that domain, taking into consideration any
restrictions or limitations placed on the individual items.

2. Does the data structure provide the necessary data access and manipulation
functionality to fully implement the ADT? The functionality of an abstract
data type is provided through its defined set of operations. The data structure
must allow for a full and correct implementation of the ADT without having
to violate the abstraction principle by exposing the implementation details to
the user.

3. Does the data structure lend itself to an efficient implementation of the oper-
ations? An important goal in the implementation of an abstract data type is
to provide an efficient solution. Some data structures allow for a more effi-
cient implementation than others, but not every data structure is suitable for
implementing every ADT. Efficiency considerations can help to select the best
structure from among multiple candidates.

There may be multiple data structures suitable for implementing a given ab-
stract data type, but we attempt to select the best possible based on the context
in which the ADT will be used. To accommodate different contexts, language
libraries will commonly provide several implementations of some ADTs, allowing
the programmer to choose the most appropriate. Following this approach, we in-
troduce a number of abstract data types throughout the text and present multiple
implementations as new data structures are introduced.
The efficiency of an implementation is based on complexity analysis, which is
not introduced until later in Chapter 3. Thus, we postpone consideration of the
efficiency of an implementation in selecting a data structure until that time. In
the meantime, we only consider the suitability of a data structure based on the
storage and functional requirements of the abstract data type.
18 CHAPTER 1 Abstract Data Types

We now turn our attention to selecting a data structure for implementing the
Bag ADT. The possible candidates at this point include the list and dictionary
structures. The list can store any type of comparable object, including duplicates.
Each item is stored individually, including duplicates, which means the reference
to each individual object is stored and later accessible when needed. This satisfies
the storage requirements of the Bag ADT, making the list a candidate structure
for its implementation.
The dictionary stores key/value pairs in which the key component must be
comparable and unique. To use the dictionary in implementing the Bag ADT, we
must have a way to store duplicate items as required by the definition of the ab-
stract data type. To accomplish this, each unique item can be stored in the key
part of the key/value pair and a counter can be stored in the value part. The
counter would be used to indicate the number of occurrences of the corresponding
item in the bag. When a duplicate item is added, the counter is incremented; when
a duplicate is removed, the counter is decremented.
Both the list and dictionary structures could be used to implement the Bag
ADT. For the simple version of the bag, however, the list is a better choice since
the dictionary would require twice as much space to store the contents of the bag
in the case where most of the items are unique. The dictionary is an excellent
choice for the implementation of the counting bag variation of the ADT.
Having chosen the list, we must ensure it provides the means to implement the
complete set of bag operations. When implementing an ADT, we must use the
functionality provided by the underlying data structure. Sometimes, an ADT op-
eration is identical to one already provided by the data structure. In this case, the
implementation can be quite simple and may consist of a single call to the corre-
sponding operation of the structure, while in other cases, we have to use multiple
operations provided by the structure. To help verify a correct implementation
of the Bag ADT using the list, we can outline how each bag operation will be
implemented:

ˆ An empty bag can be represented by an empty list.


ˆ The size of the bag can be determined by the size of the list.
ˆ Determining if the bag contains a specific item can be done using the equivalent
list operation.
ˆ When a new item is added to the bag, it can be appended to the end of the
list since there is no specific ordering of the items in a bag.
ˆ Removing an item from the bag can also be handled by the equivalent list
operation.
ˆ The items in a list can be traversed using a for loop and Python provides for
user-defined iterators that be used with a bag.

From this itemized list, we see that each Bag ADT operation can be imple-
mented using the available functionality of the list. Thus, the list is suitable for
implementing the bag.
1.3 Bags 19

1.3.3 List-Based Implementation


The implementation of the Bag ADT using a list is shown in Listing 1.3. The
constructor defines a single data field, which is initialized to an empty list. This
corresponds to the definition of the constructor for the Bag ADT in which the
container is initially created empty. A sample instance of the Bag class created from
the example checkdates2.py program provided earlier is illustrated in Figure 1.3.

Listing 1.3 The linearbag.py module.

1 # Implements the Bag ADT container using a Python list.


2 class Bag :
3 # Constructs an empty bag.
4 def __init__( self ):
5 self._theItems = list()
6
7 # Returns the number of items in the bag.
8 def __len__( self ):
9 return len( self._theItems )
10
11 # Determines if an item is contained in the bag.
12 def __contains__( self, item ):
13 return item in self._theItems
14
15 # Adds a new item to the bag.
16 def add( self, item ):
17 self._theItems.append( item )
18
19 # Removes and returns an instance of the item from the bag.
20 def remove( self, item ):
21 assert item in self._theItems, "The item must be in the bag."
22 ndx = self._theItems.index( item )
23 return self._theItems.pop( ndx )
24
25 # Returns an iterator for traversing the list of items.
26 def __iter__( self, item ):
27 ......

Most of the implementation details follow the specifics discussed in the previous
section. There are some additional details, however. First, the ADT definition
of the remove() operation specifies the precondition that the item must exist
in the bag in order to be removed. Thus, we must first assert that condition
and verify the existence of the item. Second, we need to provide an iteration
mechanism that allows us to iterate over the individual items in the bag. We delay

theItems  19
19 74
74 23
23 19
19 12
12

Bag 0 1 2 3 4

Figure 1.3: Sample instance of the Bag class implemented using a list.
20 CHAPTER 1 Abstract Data Types

the implementation of this operation until the next section where we discuss the
creation and use of iterators in Python.
A list stores references to objects and technically would be illustrated as shown
in the figure to the right. To conserve space and reduce the clutter that can result
in some figures, however, we illustrate objects in the text as boxes with rounded
edges and show them stored directly Bag 0 1 2 3 4
within the list structure. Variables theItems      
will be illustrated as square boxes
with a bullet in the middle and the Bag
name of the variable printed nearby. 19
19 74 74 23
23 1919 1212
Bag

1.4 Iterators
Traversals are very common operations, especially on containers. A traversal iter-
ates over the entire collection, providing access to each individual element. Traver-
sals can be used for a number of operations, including searching for a specific item
or printing an entire collection.
Python’s container types—strings, tuples, lists, and dictionaries—can be tra-
versed using the for loop construct. For our user-defined abstract data types, we
can add methods that perform specific traversal operations when necessary. For
example, if we wanted to save every item contained in a bag to a text file, we could
add a saveElements() method that traverses over the vector and writes each value
to a file. But this would limit the format of the resulting text file to that specified
in the new method. In addition to saving the items, perhaps we would like to
simply print the items to the screen in a specific way. To perform the latter, we
would have to add yet another operation to our ADT.
Not all abstract data types should provide a traversal operation, but it is appro-
priate for most container types. Thus, we need a way to allow generic traversals to
be performed. One way would be to provide the user with access to the underlying
data structure used to implement the ADT. But this would violate the abstraction
principle and defeat the purpose of defining new abstract data types.
Python, like many of today’s object-oriented languages, provides a built-in it-
erator construct that can be used to perform traversals on user-defined ADTs. An
iterator is an object that provides a mechanism for performing generic traversals
through a container without having to expose the underlying implementation. Iter-
ators are used with Python’s for loop construct to provide a traversal mechanism
for both built-in and user-defined containers. Consider the code segment from the
checkdates2.py program in Section 1.3 that uses the for loop to traverse the
collection of dates:

# Iterate over the bag and check the ages.


for date in bag :
if date <= bornBefore :
print( "Is at least 21 years of age: ", date )
1.4 Iterators 21

1.4.1 Designing an Iterator


To use Python’s traversal mechanism with our own abstract data types, we must
define an iterator class, which is a class in Python containing two special methods,
iter and next . Iterator classes are commonly defined in the same module
as the corresponding container class.
The implementation of the BagIterator class is shown in Listing 1.4. The
constructor defines two data fields. One is an alias to the list used to store the
items in the bag, and the other is a loop index variable that will be used to iterate
over that list. The loop variable is initialized to zero in order to start from the
beginning of the list. The iter method simply returns a reference to the object
itself and is always implemented to do so.

Listing 1.4 The BagIterator class, which is part of the linearbag.py module.

1 # An iterator for the Bag ADT implemented as a Python list.


2 class _BagIterator :
3 def __init__( self, theList ):
4 self._bagItems = theList
5 self._curItem = 0
6
7 def __iter__( self ):
8 return self
9
10 def __next__( self ):
11 if self._curItem < len( self._bagItems ) :
12 item = self._bagItems[ self._curItem ]
13 self._curItem += 1
14 return item
15 else :
16 raise StopIteration

The next method is called to return the next item in the container. The
method first saves a reference to the current item indicated by the loop variable.
The loop variable is then incremented by one to prepare it for the next invocation
of the next method. If there are no additional items, the method must raise a
StopIteration exception that flags the for loop to terminate. Finally, we must
add an iter method to our Bag class, as shown here:

def __iter__( self ):


return _BagIterator( self._theItems )

This method, which is responsible for creating and returning an instance of the
BagIterator class, is automatically called at the beginning of the for loop to
create an iterator object for use with the loop construct.
22 CHAPTER 1 Abstract Data Types

1.4.2 Using Iterators


With the definition of the BagIterator class and the modifications to the Bag
class, we can now use Python’s for loop with a Bag instance. When the for loop
for item in bag :
print( item )

is executed, Python automatically calls the iter method on the bag object
to create an iterator object. Figure 1.4 illustrates the state of the BagIterator
object immediately after being created. Notice the bagItems field of the iterator
object references theItems field of the bag object. This reference was assigned
by the constructor when the BagIterator object was created.

bagVectorcurItem
curItem
 0000

_BagIterator

theItems  19
19 74
74 23
23 19
19 12
12

Bag 0 1 2 3 4

Figure 1.4: The Bag and BagIterator objects before the first loop iteration.

The for loop then automatically calls the next method on the iterator
object to access the next item in the container. The state of the iterator object
changes with the curItem field having been incremented by one. This process
continues until a StopIteration exception is raised by the next method when
the items have been exhausted as indicated by the curItem. After all of the items
have been processed, the iteration is terminated and execution continues with the
next statement following the loop. The following code segment illustrates how
Python actually performs the iteration when a for loop is used with an instance
of the Bag class:
# Create a BagIterator object for myBag.
iterator = myBag.__iter__()

# Repeat the while loop until break is called.


while True :
try:
# Get the next item from the bag. If there are no
# more items, the StopIteration exception is raised.
item = iterator.__next__()
# Perform the body of the for loop.
print( item )

# Catch the exception and break from the loop when we are done.
except StopIteration:
break
1.5 Application: Student Records 23

1.5 Application: Student Records


Most computer applications are written to process and manipulate data that is
stored external to the program. Data is commonly extracted from files stored on
disk, from databases, and even from remote sites through web services. For exam-
ple, suppose we have a collection of records stored on disk that contain information
related to students at Smalltown College. We have been assigned the task to ex-
tract this information and produce a report similar to the following in which the
records are sorted by identification number.

LIST OF STUDENTS

ID NAME CLASS GPA


----- ------------------------- ---------- ----
10015 Smith, John Sophomore 3.01
10167 Jones, Wendy Junior 2.85
10175 Smith, Jane Senior 3.92
10188 Wales, Sam Senior 3.25
10200 Roberts, Sally Freshman 4.00
10208 Green, Patrick Freshman 3.95
10226 Nelson, Amy Sophomore 2.95
10334 Roberts, Jane Senior 3.81
10387 Taylor, Susan Sophomore 2.15
10400 Logan, Mark Junior 3.33
10485 Brown, Jessica Sophomore 2.91
--------------------------------------------------
Number of students: 11

Our contact in the Registrar’s office, who assigned the task, has provided some
information about the data. We know each record contains five pieces of infor-
mation for an individual student: (1) the student’s id number represented as an
integer; (2) their first and last names, which are strings; (3) an integer classification
code in the range [1 . . . 4] that indicates if the student is a freshman, sophomore,
junior, or senior; and (4) their current grade point average represented as a floating-
point value. What we have not been told, however, is how the data is stored on
disk. It could be stored in a plain text file, in a binary file, or even in a database.
In addition, if the data is stored in a text or binary file, we will need to know how
the data is formatted in the file, and if it’s in a relational database, we will need
to know the type and the structure of the database.

1.5.1 Designing a Solution


Even though we have not yet been told the type of file or the format used to store
the data, we can begin designing and implementing a solution by working with
an abstraction of the input source. No matter the source or format of the data,
the extraction of data records from external storage requires similar steps: open
a connection, extract the individual records, then close the connection. To aide
in our effort, we define a Student File Reader ADT to represent the extraction of
24 CHAPTER 1 Abstract Data Types

data from an external file or database. In computer programming, an object used


to input data into a program is sometimes referred to as a reader while an object
used to output data is referred to as a writer .

Define Student File Reader ADT

A student file reader is used to extract student records from external storage. The
five data components of the individual records are extracted and stored in a storage
object specific for this collection of student records.
 StudentFileReader( filename ): Creates a student reader instance for ex-
tracting student records from the given file. The type and format of the file
is dependent on the specific implementation.

 open(): Opens a connection to the input source and prepares it for extracting
student records. If a connection cannot be opened, an exception is raised.

 close(): Closes the connection to the input source. If the connection is not
currently open, an exception is raised.

 fetchRecord(): Extracts the next student record from the input source and
returns a reference to a storage object containing the data. None is returned
when there are no additional records to be extracted. An exception is raised
if the connection to the input source was previously closed.

 fetchAll(): The same as fetchRecord(), but extracts all student records


(or those remaining) from the input source and returns them in a Python list.

Creating the Report


The program in Listing 1.5 uses the Student File Reader ADT to produce the
sample report illustrated earlier. The program extracts the student records from
the input source, sorts the records by student identification number, and produces
the report. This program illustrates some of the advantages of applying abstraction
to problem solving by focusing on the “what” instead of the “how.”
By using the Student File Reader ADT, we are able to design a solution and
construct a program for the problem at hand without knowing exactly how the
data is stored in the external source. We import the StudentFileReader class
from the studentfile.py module, which we assume will be an implementation of
the ADT that handles the actual data extraction. Further, if we want to use this
same program with a data file having a different format, the only modifications
required will be to indicate a different module in the import statement and possibly
a change to the filename specified by the constant variable FILE NAME.
The studentreport.py program consists of two functions: printReport() and
main(). The main routine uses an instance of the ADT to connect to the external
source in order to extract the student records into a list. The list of records is then
1.5 Application: Student Records 25

Listing 1.5 The studentreport.py program.

1 # Produces a student report from data extracted from an external source.


2 from studentfile import StudentFileReader
3
4 # Name of the file to open.
5 FILE_NAME = "students.txt"
6
7 def main():
8 # Extract the student records from the given text file.
9 reader = StudentFileReader( FILE_NAME )
10 reader.open()
11 studentList = reader.fetchAll()
12 reader.close()
13
14 # Sort the list by id number. Each object is passed to the lambda
15 # expression which returns the idNum field of the object.
16 studentList.sort( key = lambda rec: rec.idNum )
17
18 # Print the student report.
19 printReport( studentList )
20
21 # Prints the student report.
22 def printReport( theList ):
23 # The class names associated with the class codes.
24 classNames = ( None, "Freshman", "Sophomore", "Junior", "Senior" )
25
26 # Print the header.
27 print( "LIST OF STUDENTS".center(50) )
28 print( "" )
29 print( "%-5s %-25s %-10s %-4s" % ('ID', 'NAME', 'CLASS', 'GPA' ) )
30 print( "%5s %25s %10s %4s" % ('-' * 5, '-' * 25, '-' * 10, '-' * 4))
31 # Print the body.
32 for record in theList :
33 print( "%5d %-25s %-10s %4.2f" % \
34 (record.idNum, \
35 record.lastName + ', ' + record.firstName,
36 classNames[record.classCode], record.gpa) )
37 # Add a footer.
38 print( "-" * 50 )
39 print( "Number of students:", len(theList) )
40
41 # Executes the main routine.
42 main()

sorted in ascending order based on the student identification number. The actual
report is produced by passing the sorted list to the printReport() function.

Storage Class
When the data for an individual student is extracted from the input file, it will
need to be saved in a storage object that can be added to a list in order to first
sort and then print the records. We could use tuples to store the records, but we
Another Random Scribd Document
with Unrelated Content
kansanrunoisto on jakanut muille urhoillensa. Epätietoista on, onko
tämä tragillinen henkilö lähtenyt sadusta vain jumalaistarustosta,
mutta nyt hän kumminkin monessa suhteessa on tullut samaksi kuin
tähtisulhanen, se joka tarussa Koit'ista ainoastaan kerran vuodessa
saa suudella Ämarik'in hohtavia huulia, eli joka vapauttaa Manalassa,
päällettäin ryhmitetyissä pilvissä, vangitun neidon. Vielä on
tarkastettava mitä historiallisia johtopäätöksiä Viron
kansanrunoistosto voipi saada.
III.

Historiallisia aineosia Kalevirunoistossa.

Edellisestä tutkimuksesta havaitaan kieltämättömäksi tosiasiaksi,


että virolaisessa kansanrunostossa on joukko ajatuksia ja kokonaisia
laulunkatkelmia, jotka ovat hyvin läheisessä yhteydessä samallaisten
ajatusten kanssa Suomalaisilla; onpa koko Kalevipojan olemisen ja
kohtalon pohjaluonnos yksi kuin suomalaisen Kullervon. Jos vielä
laveammalle jatkamme tutkimustamme, niin havaitaan pian
silminnähtävin yhtäpitäväisyys myös jumaluusopin suhteen, tapojen,
loihturunojen ja lumousten suhteen. Sama käsitys luonnosta ja
esineistä, sama etsiminen ilmiöiden synnyn perään tarkoituksessa
niitä sen kautta vallita, tavataan kummallakin kansalla.[58] Ja
toisenakin todistuksena molempain kansain aikaisemmasta
yhteydestä tavataan vanhemmassa kansanrunossa jäljellä olevia
pitempiä muotoja kuin mitä nykyisessä puhekielessä käytetään,
muotoja jotka ovat enemmän suomalaisen kirjoituskielen tapaisia.

Ennenkuin käyn selittämään niitä historiallisia alkuaineita, joita


erittäin Kalevipojassa voipi löytää, suotanee minun tässä tarkastaa
sitä yleisempää kysymystä virolais-suomalaisen jumalaistiedon
alkuperäisyydestä ylehensä. Nykyisissä sankari-runoissa olevat
kansantarut, näet, sisältävät ainoastaan yhden osan Wirolaisten
uskonnollisia käsitteitä, ja jos näiden muodostumisen aika
likimainkaan voidaan määrätä, niin se on tarkastuksen-alaiselle
aineelle suurimman arvoinen alkukohta.

Missä oli tämän hengellisen elämän yhteinen kätkyt, sen


sivistyksen, joka Suomalaisissa ja Virolaisissa on muodostunut
määritettyyn ja muista kansoista eriävään muotoon? Epäilemättä
kansan omalla syntypaikalla Aasiassa. Herra Santo, silloinen Wiron
kirjallisuuden seuran esimies, lausui v. 1854 kertomarunoista, että
paljon niissä "ulottuu ensimmäisellä synnyllänsä niihin vuosisatoihin,
joina näiden ranteiden periasukkaat eivät olleet yhteydessä Saksan
ritarein eikä kristinuskon kanssa";[59] ja Kreutzwald sanoo: "tämä
jättiläiskätkyt lienee etsittävä Aasiassa".[60] Viimeksi mainittu ajatus
näyttää todenmukaisemmalta. Jo ennen yhdeksättä vuosisataa
Wirolaiset ja Liiviläiset olivat levenneet yli koko Wiron-, Liivin- ja
Kuurinmaan; Vasta sen ajan jälkeen rupeavat Lettiläis-Littovialaiset
kansat ja nykyiset Kuurit heitä poistamaan.[61] Joukko paikannimiä
jerv- ja kül-päätteillä todistaa että suomalaisia kansoja ennen on
asunut Kuurinmaalla; maansa oli jaettu kihlakuntiin, kilegundæ.[62]
Tässä näkyy että suomalaiset kansat Itämeren maakunnissa olivat
ennättäneet samalle valtiollisen ja yhteiskunnallisen edistyksen
kannalle, joka kolmannellatoista vuosisadalla tavataan
Suomenmaassa olevilla. Se tapa että he kuivattavat elonsa riihissä
ennen puituansa, ja että kytömaata viljelevät, — heidän
kotoperäinen aatransa ja astuvansa eli äkeensä, sanalla sanoen
kaikki omituiset maanviljelyksen, metsästyksen, kalastuksen ja
sodankäynninkalut ja aseet ynnä niiden kotonaiset nimitykset
tavataan molemmilla puolin Suomen lahtea ja vieläpä edempänäkin
etelässä. Sellaista yhtäläisyyttä ei millään tavalla voi muutoin
selittää, kuin niin että molemmat kansat aikaisemmin ovat
alinomaisessa vuorovaikutuksessa luoneet tämän yhteisyyden
hengellisessä ja maallisessa elämässään. Ainoastaan aavistamalla
voipi arvata sen mahdottoman pitkän ajan, jota on tarvittu tämän
perinpohjaisen yhtäläisyyden synnyttämiseksi, kun noin 800 vuotta
jo on ennättänyt kulua, vaikuttamatta mitään varsinaisia muutoksia
siinä yhteisessä perinnässä, vaikka molemmat kansat siitä lähtien
ovat eläneet erillänsä.

Kohtuullisestikin arvaten lienee vähintäkin yhtä monta vuosisataa


kulunut suomalais-virolaisen sivistyksen muodostumiseen ja sillä
tavoin tulemme takaisin kansanliikkeiden edelliseen aikaan tahi
kristillisen ajanluvun alkuun. Niin kyllä on sanottu, "että nämä
molemmat suomensukuiset kansat luultavasti ovat tuoneet
vähemmän omituisia jumalais-taruja aikaisemmilta asunnoiltansa
idässä, vaan että yhdessä muiden sivistys-esineiden kanssa myöskin
jo valmiita jumalais- ja satu-kuvailuksia on heihin tullut siitä
germanilaisesta sivistyneestä kansasta, jonka kanssa he joutuivat
läheiseen keskeyteen",[63] ja tämä ajatus on erinomattain
perustettu moninaisiin yhtäläisyyksiin germanilaisten sadustoin
kanssa, ja erittäin luultuun yhtäläisyyteen saksalais-skandinavilaisien
Thor-satujen ja Wirolaisten Taaran-jumaloitsemisen välillä. Edelliset
ovat hyvin hämäräiset ja epätietoiset ja jälkimmäisistä sanoo
Fählmann kilvoituksessaan: Wie war der heidnische Glaube der alten
Ehsten beschaffen?[64] että niissä ei ole mitään yhteistä. Thor kyllä
on ukonjyryn jumala, niinkuin toisinansa Taarakin, mutta häneltä
puuttuu peräti se yksijumalaisuus, joka on niin monen suomalaisen
kansan ylimmän jumalan omituisuuksia. Kunnioituksesta hänen
oikeata nimeänsä ei tahdota mainita; Wirolaiset mainitsevat häntä
nimillä vana isa, vana taat, Suomalaiset nimittävät häntä ukoksi,
ukoksi ylijumalaksi, pilvien pitäjäksi; Samojeedeilla taas on
oikeastaan yksi jumala, Num, mutta häntä pelkäävät pohjoiset
Samojedit niin, että silminnähtävällä pelolla mainitsevat hänen
oikeata nimeänsä, vaan mieluisemmin käyttävät liikanimiä; Tomskin
Samojedit taas kutsuvat häntä nimellä Ildscha, ukko, äijö, sanoo
Castrén.[65] "Hän näkee ja tietää kaikki, mutta on liian yleväinen
tahtoaksensa katsella alas ihmissuvun onnen vaiheisin".[66] Aivan
samalla lailla Ostjakit käsittivät ylimmäisen jumalansa Türm. Hän
"puhuu ihmisille ainoastansa ukkosen ja myrskyn vihaisella äänellä.
Häneltä ei ole maailmassa tapahtuvat hyvät eikä pahat teot salassa,
ja hän tekee välttämättömästi jokaiselle oikeutta, mutta sittenkin hän
on ihmisille lähestymätön ja hyvin pelättävä olento".[67] Sekä
nimitys että käsitys tässä on yhtä kuin Virolaisten Taara ja vielä
likemmin kuin Tora, joka Tshuwaschin kielellä merkitsee koko
maailman jumalata; tähän kuuluu Lappalaisten Tiermes, eräs taivaan
jumala.[68] Virolaiset käyttävät lauseen vana taat torises,[69]
vanhus torua, ukkosen jyrinästä, ja tämä johdattaa meidät siihen,
että suomalainen tora, torua, kieliopillisesti on heimoa mainitulle.
Schott lausuu että Tschuwaschien Tora, heidän kielensä
äänisääntöjen jälkeen, aivan hyvin on voinut johtua tuosta melkein
kaikissa Turkkilaisissa kielissä yhteisestä sanasta Tangry.[70]
Selvimmin tavataan kaikkien näiden nimien juuri Karagasseilla, jotka
sanalla têre tarkoittavat taivasta, sanalla dêr, ukkosen jyryä. Edellä
munitussa lienee siis oiva todistus siitä, että suomalaiset kansat
olopaikoillaan Aasiassa ovat tuoneet kanssansa suuren joukon
jumalaistarullisia ajatuksia, koska sellaisia vielä voipi niin kaukana
toisistansa olevissa kansoissa löytää. Mutta muitakin yhteisiä
ajatuksia löytyy, esim. pyhästä suuresta puusta, elämän vedestä,
jolla eräs Samojedi voitelee vanhempiansa, niin että ne jälleen
virkoovat eloon, tavataanpa yksin muinaisrunojemme kerto ja alku-
sointu, jota seikkaa erinomattain on tahdottu pitää todistuksena
lainasta läheisiltä Germaneilla. Mongolien heikoissa runoyrityksissä,
niinkuin Pallas ne esittää, näytäksen lävitsensä kertoa ynnä
alkusoinnun ja yksin loppusoinnunkin eli riimin jälkiä. Voimatta
ratkaista kysymystä näiden runojen alkuperäisyydestä, pidän
enemmän lukua siitä, että myöskin tatarilaisien Koibalein
urosrunoissa tavataan vielä suurempaan määrään kertoa ja alku-
sointua, sekä, ja se on paljoa painavampi, heidän kertomarunon ja
rakennus näyttää tehdyltä pitkälyhyen nelimitan pohjalle, samoilla
runojalkain vaihtelemisilla kuin suomalaisessa kansanrunossa.[71]
Tässä lienee yksi tukevimpia todistuksia itsenäisestä, jo Aasiassa
aljetusta, Altailaisten kansain jumalaistarujen ja satujen
kehkiämisestä, vaikkapa tekoaineita on kerättävä paljon, ennenkuin
niiden laveutta ja ensimmäistä alkuperää voinee selittää.

Toiselta kannalta näyttävät jumalaistarulliset tutkinnot Saksassa


johdattavan samaan päätökseen. Kuhn on huolellisesti toimitetussa
teoksessaan: "Die Herabkunst des Feuers und des Göttertranks",
osoittanut sitä eroa, joka Aasialaisessa kansasuvussa on
muinaisuudessa mahtanut tapahtua, jolloin Indian ja Persian kansat
erkenivät toisistansa, edelliset yhäti enemmän kehittäen
ulkokohtaista luonnonnäkemystänsä, Persialaiset taas itsekohtaista.
Germanilaisten esi-isät heidän eretessään olivat lähempänä
Persialaisia, ja heidän kaksisuuden (dualismon) kaltainen
maailmannäkemys oli sentähden periaatteellinen vastakohta
suomalaisten kansain tarkasti merkitylle kaikkijumalaisuudelle
(pantheismolle). Se on kaikissa Altailaisissa kansoissa niin juurtunut
koko heidän ajatuspiirihinsä ja kieleenkin, että ne sen kautta selvästi
rajoitetaan germanilaisista kansoista. Ja kuitenkin kumottaa etempää
se salainen perustus, se hämäräinen yliolento, jonka nimeä, niinkuin
Juutalaisillakin, ei rohjettu mainita, — mukava liitoskohta pian
tulevalle kristinuskolle. Vaikeus, jolla, tuossa Altailaisten kansain
muinaisuutta peittävässä hämärässä, selviä piirteitä voidaan keksiä,
enenee siitä, että heimokansain jumalaistarut ja perivanha sivistys
häviää vieraiden uskontoin vaikutuksesta. Unkarilaisissa on
kristinusko, Turkkilaisissa Islam, ja useimmissa Aasian kansoissa on
Buddhanusko hävittänyt heidän alkuperäisen uskontonsa. Kenties
vastedes vielä onnistuu löytää merkittäviä muinaisen luonteen jälkiä.

Tällä ei kuitenkaan ole sanottu, että suomalaiset kansat olisivat


täydellisesti olleet vapaat kaikesta vaikutuksesta niiltä kansoilta,
joiden kanssa he ovat olleet yhteydessä. Päinvastoin voidaan
sellainen vaikutus löytää kaikkialla ja mahdotonta on ajatellakaan,
että joku mannermaan kansa muinoinkaan olisi kiinalaisella muurilla
voinut sulkea luotansa kaikki sivistysaineet läheisistä kansoista.
Niinkuin vieraita rahoja otettiin vastaan, niin seurasi jokaista yhteyttä
myöskin uudet sanat ja ajatukset, jotka viimeiset olivat vielä
suuremman arvoiset, sillä ne voitiin enentää moninvertaisesti.

Hyvin selviä jälkiä sellaisesta keskuudesta Skandinavian kanssa


voidaan nähdä Virolaisten jumaluusajatuksissa ja erittäin tässä
tutkitussa runoistossa. Edellä olen maininnut uudemmasta Eddasta
lainatun kertomuksen Thorista ja jättiläisvaimosta, joka on muutettu
Kalevipojan osaksi. Jo Lencqvist mainitsee suomalaisessa
jumalaistarustossa olevan nimityksen tonttu, kotijumalan, joka
toimitti kaikellaisia etuja;[72] Virolaisissa siihen vastaa tont.
Molemmat ovat lainattuja Skandinaveilta, joilla häntä kutsutaan
tomtegubbe, tomt.[73] Tässä en pidä mitään lukua sellaisista
ajatuksista, jonka kaltaisia kyllä tavataan germanilaisissa ja
romanilaisissa kansoissa, mutta joihin vastaavia ajatuksia myös
löydetään Virolaisten heimokansoissa Aasiassa tahi Etelä-
Euroopaasa. Niin esim. pitävät Virolaiset horkkaa eli vilutautia
harmaana tahi valkoisena hevosena, jolla potilas ratsastaa; Tatarit ja
Kalmukit kuljettavat sairasta ratsain täyttä laukkaa, kunnes hevonen
tulee vaahteen; sitten hänet peitetään hyvästi ja hän paranee
tavallisesti.[74] Saksalaiset ajattelevat kuumeen ratsastuksena
hurjalla hevosella.[75] Sellaiset ajatukset voivat joko itsenäisesti
syntyä useissa kansoissa, tahi, niinkuin niiden muutamain sanojen
on laita, jotka ovat Lättiläisten ja Virolaisten kielissä yhteisiä, ovat
tulleet aikaisemmasta keskeydestä ja ovat niinmuodoin jäännöksiä
ennenhistoriallisesta aikakaudesta, etten sano alkuperäisestä
yhteydestä.[76] Mutta Virolaisilla tavataan paljo ajatuksia, jotka
silminnähtävästi ovat syntyneet vasta keskeydestä Skandinavein
kanssa myöhemmällä ajalla, jolloin kristillisiä näkemystapoja jo oli
juurtunut kansoihin. Sellaisiin kristillisiin alkeisin on luettava
nimitykset Sarvik ja Tühi. Ylimalkain on virolaisissa saduissa maanala
muodosteltu paljon toisenmoiseksi, jouduttuansa keskeyteen
helvetinopin kanssa. Suomalaisissa taruissa vielä on ainoastaan
maanalan hallitsijan nimi jolloinkulloin muuttunut kristilliseksi, nim.
Pahaseksi, mutta muutoin on pakanuuden ajatustapa kaikessa
voimassaan säilynyt ja elämä maanalassa on melkein samallaista
kuin maan päällä. Kristinusko on vaikuttanut muutoksen, on tehnyt
perkeleen paljoa pirullisemmaksi, ja se omaisuus tavataan
selvempänä Suomen lahden eteläpuolella. Sentähden onkin
Virolaisten pirulla sarvet, jota koristusta Suomalaiset eivät vielä ole
hänessä huomanneet. Kumminkin on erinomaisen vaikeata tarkoin
eroittaa niitä alkeita kansantarustossa, jotka kuuluvat kristinoppiin,
pakanallisista, ja vielä vaikeampi on määrätä mitkä viimemainituista
ovat alkuperäisiä. Kalevipojan matkoja maanalaan tutkittansa kohtaa
sama vaikeus, sillä samanlaisia matkoja jossakin tarkoituksessa
tavataan melkein kaikilla kansoilla. Suomalaisilla ja Kreikkalaisilla,
Indialaisilla ja Roomalaisilla tapaamme sellaisia kertomuksia.
Kiinalaiset kertovat nuorukaisesta, joka sieltä vapauttaa äitinsä;[77]
Mongolit ja Tibetiläiset antavat mainion urhonsa, Gesar Khan,
sotatapparallansa särkeä helvetin portit ja sieltä vapauttaa äitinsä,
sekä lähettää hänet taivaasen.[78]

Tässä suhteessa valaisevia ovat ne sadunkatkelmat, jotka ovat


kerätyt Viron saarien muinaisruotsalaisissa asukkaissa. Worms-
saarella, jossa asuu osaksi Ruotsalaisia, osaksi Virolaisia, kertovat
edelliset merimiehestä, joka niinkuin Kalevipoeg matkusti syvässä
holvissa ja sieltä toi muassansa kolme säkillistä rahoja. Vanha Jakob
de la Gardie, Hiiomaan eli Päiväsalon ja Wormsin herra, sanotaan
kerran, kun vihollisia äkisti karkasi hänen päällensä, juosneen
alastomana saunasta, ainoastaan polstari kädessä, jonka höyhenistä
hän ravisteli sotilaita, niinkuin "Soomen tuuslar"; puhutaan myös
jätinkokoisista munkeista, viroksi munga rahvas, jotka paljaalla
vainullaan etsiskelevät kristittyjä niitä tappaakseen, jotka kaikki
luulot ynnä monta muuta samallaista kohtaavat meitä
Kalevirunoissa.[79] Ovatko nämä alkuperäisin virolaisia vai
ruotsalaisia, sitä ei kuitenkaan tästä nähdä. Sellaiset kertomukset
kulkivat enimmäksensä suusta suuhun, mutta kirjoittamallakin tieto
niistä leveni. Hyvin huvittava taas, ja todistava miten Euroopan
useimmissa maissa tutut kertomukset pyhistä miehisiä myös
tunkeutuivat suomalaisiin kansoihin, on Russwurmin ilmoittama tieto,
että useissa Länsi-Wiron pitäjäissä käsikirjoituksina kulkee virolaisia
kertomuksia Jesuksen lapsuuden historiasta, evangelium infantia.
Toinen virolainen käsikirjoitus, joka on löydetty Nucköön
Ruotsalaisissa, sisältää monta esitelmää, joista ensimäinen, Jesuksen
pyhän äidin unennäkö, on meille tärkeä. Kun sadut Kalevipojasta
eivät näy kärsineen mitään erinäistä vaikutusta Jesuksen lapsuuden
historiasta eikä muista viimemainitussa käsikirjoituksessa olevista
esitelmistä, tavataan tässä esitelmässä melkein kuva kuvaltaan
samaa, mikä Kalevipojan matkassa Manalaan on merkillistä. Tässä
kirjoituksessa nimittäin, joka muutamissa osissa pitää yhtä
Nikodemon evangeliumin kanssa, sanotaan: Sinä laskeuit alas
helvetin hautaan, syvyyteen, põrgo haua, sügavusse sisse, ja mursit
helvetin kaikki rautaveräjät ja ukset, keik põrgo raudaväravad ja
uksed — lochurás tás túlas tás chalkéas, kai tous móchlous tous
sidaerous — otit pois kaikki helvetin siteet, võitsid keik se põrgo vöö
ära, — oi dedeménoi kántes nekroi éluthaesan ton desmon — ja
sidoit ikuiseksi ylimmäisen saatanan, seda edina saadanad — ton
árchisatrataen satán. Sitte sinä veit kaikki hurskaat sielut ja vainajat
pyhät helvetistä j.n.e. Siihen lisätään, että se, joka väärentämättä
säilyttää luonaan tämän unen, varjellaan sen kautta kaikellaisesta
pahasta, ja joka kirjoittaa sen, sille annetaan syntinsä 40 kertaa
yhtenä päivänä anteeksi.[80] Viimemainitut lisäykset osoittavat
kertomuksen kuuluvan katoliseen aikakauteen.

Mutta vielä merkillisempi on eräs loihturuno, joka katolisuuden


ajalla on kulkenut Saksasta ja Skandinavian maista Wiron ja Suomen
kaukaisimpiin osiin. Kreutzwald on tutkijoille huomauttanut mikä
eroitus ylimalkain on loihturunojen ja loihtutavan välillä
Germanilaisissa ja Suomalaisissa kansoissa. "Germanilaisten
loihtimisissa pitää noita itsensä taudin eli vian herrana, ainoastaan
sen vuoksi että hän osaa johtaa parannustapansa, loihtunsa takaisin
johonkin alkuperäisin jumalasta lähteneesen tekoon." Tämän teon
arvellaan vieläkin jatkauvan ja loihtija asettaa esilläolevan tapauksen
sen alle. Tahi hän yhdistää taudin johonkin luonnon ilmiöön ja
toivottaa sen kulkevan samaa tietä. Suomalaisissa loihtimisissa sitä
vastoin osoitaksen tunto siitä, että loihtija tietonsa nojassa, sen
sanan avulla, joka on esineelle antanut alun, hallitsee sitä, yksinpä
henkiäkin.[81] Edellistä laatua on se kappale, jonka tahdon
vertailemisen vuoksi tähän ottaa, vaikka se oikeastaan ei kuulu
tämän tutkistelmuksen alaan, koska sitä ei ole Kalevirunoistossa.
Kutshinan kylässä Pihkowan lääniä on nimittäin Kreutzwald tavannut
seuraavat sanat niukahtumista vastaan:

Jeesus kirike mineksi


Eeruselle, aaruselle,
Lõhe musta moorusella.
Kala iirikarvaselle
Nikastes obuse jala.
Jeesus maha rattaalla
Obo jalga lausumaie:
Siit on liige nikastanud
Siit on soone songatanud,
Siit on joose jongatanud.
Mingo liige liigete vasto,
Mingo sooni soonte vasto,
Mingo joose joosete vasto,
Mingo luu luude vasto,
Liha lihade vasto:
Maarja määrgo märga peale!
Isa meie etc.

Toisinto samoilta paikoilta kuuluu:

Ehk on viltind, ehk on vältind,


Viltind, vältind väänula:
Taara tarkus, Marja märkus,
Sinisega siduda,
Punasega punuda
Kollasega koko panna,
Ehk on, isand Jumal,
Sino ja mino tahtmine!
Kolmas toisinto, jonka myös Kreutzwald on kerännyt P.
Pietarin pitäjästä Järwa-maalla, kuuluu seuraavaisesti:

Jeesus kiriko minnija lljuselle, aljuselle, Maa musta


mudasella. Sohvia ei soovitamaie, Maarja jäi maale rattaalla:
"Koko nahk, koko liha, Koko sooned, koko luud, Koko liikmed,
koko jäkkud!" Siis niksus; siis naksus. Abi keel, abi meel, Abi
armas Jumalalta! Aita Maarjat! Tagane vaenlased ja vastosed!
[82]

Kreutzwald asettaa tämän kanssa yhteyteen ne loihtusanat


jalkansa nyrjähdyttäneen hevosen yli, jotka Grimm kertoo tuosta
mainiosta Merseburgin käsikirjoituksesta, ja jonka Grimm luulee
olevan syntyjään 10:nestä vuosisadasta. Alkuperäiset pakanalliset
jumalat ovat siinä vielä jälillä, niinkuin ruotsalaisessakin toisinnossa,
norjalaisessa ja skottlannilaisessa niiden sijaan sitä vastoin on
asetettu kristittyin Jumala. Tähän minä ne panen toisen toisensa
jälkeen. Toiset sanat Merseburgin käsikirjoituksessa kuuluvat näin:

Phol ende Wodan vuorun zi holza, do wait demo Balderes


volon sin vuoz birenkit; do biguolen Sinthgunt, Sunna era
suister, do biguolen Frua, Folla era suister, do biguolen
Wodan, so he wola conda, Sose benrenki, sose bluotrenki
Sose lidirenki,… ben zi bena, bluot zi bluoda, lid zi giliden,
sose gelimida sin.

Wodan on parantanut Balderin sälön lausumalla (bigalen); tämän


laulun lausuminen nyt parantaa toisia; ja mitä muut eivät mahda,
sen mahtaa Wodan. Lyhyemmältä lauletaan vielä moniaissa Ruotsin
maakunnissa, flåg-nimistä hevostautia paranneltaessa:
Oden står på berget, han spörjer efter sin fole, floget har
han fått. — Spotta i din hand och i hans mun, j.n.e. han skall
få bot i samma stund.

Eräs toisinto alkaa näin:

Frygge frågade frå: huru skall man bota den floget får?

Norjalainen laulu on, kuten sanottiin, jo muuttunut kristityksi, vaan


käytetään kuitenkin. Siinä nähdään se vanha, tuttu tapaus, että
pakanallisten jumalain nimet häviävät, vaan heidän tekonsa
muutetaan uudelle nimelle, joka nyt pidetään suurena ja pyhänä:

Skottlannilainen laulu kuuluu:

The Lord rade, and the foal slade; he lighted, and he


righted. Sent joint to joint, bone to bone, and sinew to sinew.
Heal in the holy ghost's name!

Norjassa vielä lauletaan:

Jesus reed sig till hede, da reed han sönder sitt folebeen.
Jesus stigade af og lägte det: Jesus lagde marv i marv, Been i
been, kjöd i kjöd, Jesus lagde derpaa et blad, att det skulde
blive i samma stad.

Grimm, joka on verrannut nämä germanilaiset sanat,[83] syystä


tarkastuttaa mieleen, kuinka parantaminen heissä kerrotaan melkein
samoilla sanoilla, joka on todistuksena heidän vanhuudestansa
Germanilaisissa. Ne mahtanevat perustautua mahtavaan tarustoon,
koska ne ovat voineet sellaisella tarkkuudella säilyä moninaisten
sivistyskausien läpi. Germanilaisten kanssa, heidän sotiessansa
kristinuskon levittämiseksi, leveni tämä ja luultavasti moni muu
samanlaatuinen loihturuno muihin kansoihin. Sitä tietä Wirolaisetkin
lienevät oppineet sitä tuntemaan, ja se on, luultavasti samalla lailla,
— levinnyt suurimpaan osaan Suomenmaata. Lencqvistin
tutkistelmuksesta: De superstitione veterum Fennorum (siv. 90)
Kreutzwald jälkilauseessa äskenmainittuun teokseensa, julkaisee
seuraavan loihturunon:

Kiesus kirkkohon menepi,


Maria Messuhun matapi,
Hevoisella hirvisellä,
Kala hauvin karvaisella,
Lohen mustan muotoisella.
Ajoit sildoa kivistä,
Nousit vuorta korkiata:
Niveltyi hevoisen jalka.
Maria maahan rattahilta
Suonia sovittamahan,
Pahoja parantamahan.
Jost' on liha liipahtanut,
Siihen liha liittyköhön.
Jost' on suonet sortunehet,
Siihen suonet solmikkohon
Ehommaksi ennellistä,
Paremmaksi muinohista.

Kanslianeuvos Lönnrotin hyvyydestä olen saanut tilaisuutta tähän


liittää vielä kolme muuta saman loihturunon toisintoa, jotka ovat
kerätyt viideltä eri seudulla maatamme, ja toistaiseksi säilytetyt
Suomalaisen Kirjallisuuden keräyksissä, 22:nnessä siteessä.

Toisinto Ikaalisista, B.A. Paldanin keräämä:


Jesus ajo hevosella,
Hevosella hiirikolla,
Kivisellä kirkkotiellä;
Hivelty hevosen jalka.
Jesus maahan ratsahilta,
Suonia sovittamahan,
Lihoja liittelemähän,
Suuret suonet suutasuin
Pienet suonet päätäpäin.
Jos on ennen pahoin käynyt,
Tehköön Jesus terveheksi,
Sisältä kivuttomaksi,
Päältä tuntumattomaksi,
Paremmaksi entistänsä.

Sit. 22: 20.

Toisinto Honkojoelta, B.A. Paldanin keräämä:

Jesus ajo kirkkohon


Hiirikolla hevosella,
Kalan hauven karvaisella,
Lohen mustan muotosella.
Hiveltypä hevosen jalka
Kivisellä kirkkotiellä:
Lähde Maaria maahan,
Suonia sovittamahan,
Jäseniä jatkamahan,
Sisältä kivuttomaksi,
Päältä tuntumattomaksi.

Sit. 22, siv. 52.


Melkein sanalta sanaan näin kuuluvan on sama keräilijä saanut
Karvialtakin.

Toisinto Kuhmoisista, melkein sanalta sanaan yhtäkuuluva kuin


toinen Sotkamolta, O. Pettersonin saama, sit 22: 86, ja kuin yksi,
joka on otettu Suomen kansan vanhoihin runoihin S. Topeliukselta, V,
47.

Kuhmoisista saatu kuuluu:

Jesus kirkkoon meneepi


Hevosella hirvisellä,
Kalahauvin karvasella,
Lohen mustan muotosella.
Hivelty hevosen jalka,
Tarttu hevosen seäri-varsi
Kivisellä kirkkotiellä,
Vaskisella vainiolla.
Jesus maahan rattahilta:
Mist' on suoni solkahtannu,
Mist' on liikunnu lihoa,
Siihen liittele lihoa,
Siihen pane palliota.
Neitsy Maaria emonen!
Tuopa silkkisi sininen,
Paitasi tulipunanen.

Sit. 22: 105.

Tälle loihturunolle olen antanut jotenkin lavean tilan tässä


tutkistelmuksessa, mutta se oli tarpeellista siksi että saataisiin
yleinen katsaus sen äärettömästä yleisyydestä eri kansojen taruissa,
ja että voitaisiin vertailla sen monet toisinnot keskenään. Heti
näytäksen, että kaikista näistä toisinnoista suomalaiset ja virolaiset
likimmin ja useimmissa lauseissaan pitävät yhtä, ja niiden kanssa
samanlaatuisimmat ovat Norjassa vielä tunnetut sanat. Suomalaiset
ja Wirolaiset siis ovat mahtaneet oppia sitä tuntemaan joko silloin
kun heidän yhteytensä vielä oli suurempi, kun kansan virtailus
Suomenlahden molemmille puolille ei vielä ollut la'annut, vaan kun
sittemmin jakautuneiden Hämäläisten parvia vielä asuskeli Laatokan
ja Äänisjärven rannoilla, s.o. viimeistäkin noin 12:lla vuosisadalla,
tahi voivat he sen oppia kanssakäynnin kautta saman kansan keralla,
tahi myöskin on tämä yhteys voinut tapahtua myöhemmin
kummassakin kansassa erikseen. Missä hyvänsä johtaa meitä
lähimmäinen yhtäläisyys toisintoin välillä Skandinavialaisiin, ja niistä
olivat Ruotsalaiset se kansa, jonka kanssa sekä Suomalaiset että
Wirolaiset olivat kanssakäymisessä. Kolmannellakin lailla olisi se
voinut tapahtua, nimittäin sen kanssakäynnin kautta, joka
vanhemmilta ajoilla on Suomenlahden poikki ollut Wirolaisten ja
Suomalaisten välillä, ja josta Kalevipoeg itse monessa kohden
todistaa. Ainakin on tämä vaikutus Ruotsista mahtanut tapahtua
katolisuuden ensimmäisen ilmestymisen ajoilla näille paikoin.
Wirolaisissa, sekä Pihkovasta että Järvalta saaduissa kappaleissa,
tavataan nimittäin lla-päätteinen adessivomuoto; ja vaikka kyllä on
merkillistä, että Wirolainen runoudessaan useasti käyttää
täydellisempiä muotoja kuin puhekielessä, niin tätä sijaa ei
kuitenkaan tavata. Se kohta, että kaksi toisintoa on saatu Pihkovan
seuduilta, jo itsessään näyttää todistavan vanhempaa ikää. Se seutu
on, mitä laulurikkauteen tulee, niinkuin saari erillään muista osista
maata, sillä tavoin että Tarton piiri on aivan ilman runoja ja taruja.
Kielessäkin tavataan se omituisuus, että se vanhemmissa runoissa
on enemmän Räävelin, puheessa Tarton murteen tapaista, ja erittäin
sanotaan sen olevan Suomen kielen tapaista Kutshinan kylässä.
Kummastuttava on myös viimemainitun seudun runoissa tieto
paikkakunnista Suomenlahden rannoilla, vaikka yhteys niiden kanssa
on hyvin vähäinen. Tämän kaiken johdosta on syytä Kreutzwaldin
mukaan päättää tämän kansan Pihkovan ympärillä tuoneen runonsa
muassaan entisiltä asuinsijoiltaan Suomenlahden rannoilla.[84]
Muutos on mahtanut tapahtua hyvin aikaisin, koska nyt ei enää ole
mitään yhteyttä entisten asuinpaikkojen kanssa, ja koska
muinaistarutkaan eivät siitä säilytä mitään kertomusta. Varmana
taidettaneen pitää, että runotaito on perintö muinaisilta ajoilta,
mutta milloin mainitut loihtusanat lienevät levinneet ympäri
Pihkovan, Järvan ja Suomenmaiden, siitä ei tämä anna selkoa. Ehkä
se lienee tapahtunut silloin kun Ruotsalaisilla katolisuuden aikana oli
Wironmaassa suurempi valta kuin sittemmin, ja kun Inkerinmaa
pidettiin ikäänkuin välikkönä Suomen- ja Wironmaiden välillä.
Historia tietää, näet, kertoa moninaisista Ruotsalaisien retkistä
syvään Liivinmaahan vielä 13:llakin vuosisadalla, ja ruotsalaisten
siirtolaisten asuminen ainakin länteisessä Wirossa on samasta ajasta
asti historiallisesti todistettu.[85]

Sen vertaa niistä ajatuksista, jotka osaksi kristinuskon kanssa,


tunkeutuivat Wirolaisiin heidän kanssakäyntinsä kautta
Skandinavialaisten kanssa. Toinenkin tärkeä lisäys Wirolaisten ja
Suomalaisten aikaisemman sivistyksen historiaan on niissä monissa
sanoissa, jotka he ovat ottaneet kielehensä Ruotsista. Yleinen syy
tähän on se, että uusien esineiden ja käsitysten kanssa myöskin
niiden nimet lainattiin, ja kun Wirolaiset ja Suomalaiset olivat
melkein samalla sivistyksen portaalla, olivat lainatkin samalaiset.
Siinä on kuitenkin se omituinen seikka, että paitsi näitä tavataan
molemmin puolin Suomen lahtea paljo lainattuja sanoja, joiden
merkityksille ainakin Suomen kirjakielessä on syntyperäisetkin nimet.
Tässä luettelen paremman vertauksen vuoksi ne vieraat sanat, jotka
Kalevirunoistosta olen kerännyt:

Vir. = Suom.

agan = akana, Vatjan kieleksi akana[86] adra = aura, Vat.


adra õlut = olut ohverda = uhrata kambri = kammari kaku =
kakku (leipänen) kaape = kaapu kampi = kampa kannu =
kannu (haarikka) katel = kattila, Vat. kattila klaasi = klasi, lasi
kepi = keppi (sauva) Vat. keppi kelderi = kellari kilinki =
killinki kinga = kenkä kiriko = kirkko, Vat. cerikko kööki =
kyökki humal = humala, Vat. umala taaleri = taaleri tantsin =
tansin talli = talli tarbe = tarve, tarpehen tooli = tuoli torni =
torni turku = turku tükki = tykki (ruukki y.m.) tünderi =
tynneri (pulkko) paari = pari panti = pantti peekeri = pikari
Vat. pikari penningi = penninki piiga = piika Vat. piika piipu =
piippu püksi = pöksyt (housut) prõua = rouva, frouva vangi =
vanki viina = viina värvita = värjätä võlvi = holvi saagi = saha
sadula = satula sammeti = sammetti siidi = vertaa silkki soola
= suola, Vat. soola sänki = sänky riida = riita (taistelu) riigi =
riikki (valtakunta) raamat = raamattu moori = muori marssi =
marssi müüri = muuri nööri = nuora? lampi = lamppu tüüra =
tyyrätä, laskea, ohjata literi = liiteri, vaja, katos lodja = lotja,
saima, soima looti = luoti, puntti, punnukka, paino lusti =
lysti, halu, huvitus.

Sanoja, jotka ovat yhteisin, mutta joilla Suomessa on syntyperäiset


imitykset:

ämbri = ämpäri, sanko kaupa = kaupata, ostaa kiilu = kiila,


palkki, telkin, küüran = kuurata, pestä (laattiaa) kruusi =
ruusi, kivi-astia, pottu tinga = tingata, tilata, pyhittää paadi =
paatti, vene piina = piinata, vaivata, kiduttaa, rääkätä prundi
= prunti, runti, tulppa vilti = viltti, huopa märki = merkki, jälki
mütsa = myssy, lakki narra = narrata, vietellä, houkutella
röövel = ryöväri, rosvo, voro.

Erittäin saksalaisia sanoja ovat:

arsti = arzt undruka = unterrock kooki = kuchen krabista =


krabbeln kuninglik = königlich krammike = schramme lövi =
löwe meister = meister tressid = tressen.

Venäjän kielestä lainattuja sanoja ei ole tässä otettu tulkittaviksi.

Mitenkä tämä yleinen yhtäpitäväisyys sellaisissakin nimityksissä,


jotka näyttävät suotta lainatuilta, on selitettävä? Etenkin kun
tiedetään että ne Suomalaisilla paraittain tavataan Hämeen
murteessa. Sitä ei voi luulla että Hämäläiset enemmän kuin
Karjalaiset olisivat tarvinneet niitä, s.o. että olisivat olleet ilman niitä
käsityksiä, jotka jälkimäisillä oli, ja niinmuodoin olisivat olleet
alemmalla sivistyksen portaalla. Tahi jos ne taas ovat mielivaltaisesti
ja ikäänkuin tottumuksesta ilman tarkempaa ajatusta säilytetyt, kun
muukalaisten useammin kuultiin niitä käyttävän, miksi ne silloin
olisivat yhteisiä? Ja mistä se tulee että tässä luetellut sanat
molemmissa kansoissa tavallisimmasti käytetään? Kaikkiin näihin
kysymyksiin saataneen tyydyttävä vastaus, jos ajatellaan että
länsisuomalaisen suvun Jämiläinen osa, johon kuuluu Wirolaiset,
Liiviläiset, Watjalaiset, Wepsäläiset ja Hämäläiset, aikaisemmilla
asuntapaikoillaan Laatokan, Äänisjärven ja Walget-järven seuduilla
ovat olleet pitkällisen vaikutuksen alaisina siellä asuvain
Skandinavilaisten puolesta. Alkulauseessa Watjalaiseen kielioppiinsa
on Ahlqvist antanut erinomaisen valaisevan esittelyn mainittujen
kansain yhteydestä.[87] Sekä sananjohto- että muoto-oppinsa
osoittavat nimittäin, että ne jo ennen eroamistansa olivat
muodostaneet ne omituisuudet, jotka nykyjään selvimmin ovat
nähtävät Wirolaisissa. Mutta jo 12 ja 13 vuosisadalla he olivat
levinneet yli Liivinmaan ja länteisen Suomen, sentähden on
puheenalaisen Skandinavilaisten vaikutuksen täytynyt jo sitä ennen
tapahtua. Tällä tavoin me johdumme siihen aikaan, jona Warägein
valta kukoisti Pohjois-Wenäjässä. Tuskin lienee yhtään aikaa, jolloin
Skandinavialla olisi ollut niin suuri valta koko Euroopan asioihin kuin
silloin. Wiron ja Liivin maat olivat jo 9 vuosisadalla sen yhteydessä,
Skandinavian kauppiaat harjoittivat pitkin Wäinäjokea (Düna)
Ostrogard'in ja Kiev'in poikki vilkasta kauppaa Konstantinopolin ja
Kreikanmaan kanssa; toinen kauppatie kulki Laatokkaa, Welhojokea
(Wolchov), Dnieperiä ja Wolgaa myöten; Rurik veljinensä ja
Rôskansan kanssa perusti v. 862 Novgorodin mahtavan valtakunnan;
Skandinavialainen Rogvolod perusti v. 980 ruhtinakunnan Polotzk'iin,
joka kesti aina vuoteen 1129, jolloin Warägit sieltä ajettiin
Byzantioon.[88] Mihinkä arvoon he siihen aikaan olivat nousneet,
todistaa historioitsija Nestor, kun kutsuu Itämerta Warägien mereksi.

Aivan epäilemättä tulivat heimokansamme, jotka asuivat


mahtavain vikingein itäisen vallan keskuspaikan likitienoilla, juuri
tällä Skandinavialaisten loistoisalla ajalla heidän kanssa yhteyteen.
Paitsi sitä mitä jo ennen on mainittu, osoittaa nimittäin vielä se
maatieteellinen tieto, jonka Wirolais-Suomalaiset runoudessaan ovat
säilyttäneet, että he siihen aikaan, jolloin heidän kansain-
nimityksensä syntyivät, asuivat aivan rinnattain. Kaikki vieraiden ja
omienkin maapiirien nimet ovat nimittäin yhteisiä. Wirossa ei mainita
ainoasti Kuura ja Läti maa, vaan myös Poola, Wenä ja Saksa, Viru ja
Soome, yksinpä Turjakin. Vaan näiden nimitysten synnyn-aikan
valaisee enimmin Rootsi, Ruotsi = Rôs, Rûs, jolla Wirolaiset ja
Suomalaiset vieläkin osoittavat Ruotsalaisia. Syynä ei voi olla muu
kuin että nämä silloin nimittivät itsensä samanlaisella nimellä, ja
historiasta saamme tietää että Warägit juuri 9 ja 10 vuosisadoilla
olivat nimellä Ros avaralla tunnetut, etenkin itäisessä Euroopassa.
Mitä runo itse kertoo siitä ankarasta taistelusta, jossa Wiron kansa
näkyy kukistuvan Saksan ritarien ja päälletunkeutuvien Wenäläisten
alle, koskenee moniaissa osissa hyvin kaukaista muinaisuutta, jolloin
Jämein suku Wirossa ja se osa sitä, joka oli vetäynyt Suomeen vielä
pitivät itsiänsä veljeksinä, joilla oli samat edut puolustettavana. Kun
nimittäin Kalevipoeg Emajoen rannoilta kutsuu sotilaat kokoon
vastustamaan ritareita, riaitlitega, jotka ovat rautaan puetut
raudariides, niin ne kokoutuivat, ei ainoastaan Wiron (Virust 500) ja
Liivinmaiden eriosista, vaan myöskin Kurjensaarelta, Kuresaare, tuli
600 ja Suomesta 700. Sotajoukko kulki itään, jossa se
monenpäiväisen tappelun jälkeen voitettiin. Vaikea on päättää kuinka
paljon erityisiin ilmoituksiin on luotettava ja mitkä niistä ovat
toisihinsa verrattavat. Kokonaisuudessaan ne kumminkin näkyvät
osoittavan jälkiä aikakaudesta, jolloin Jämein kansasuku Suomen
lahden itärannalla ja eteläpuolella joutui kanssakäyntiin Warägein
valtakunnan kanssa ja sen sotaisia hallitsijoita vasten koetti varjella
itsenäisyyttään.

Talla viimeisellä seikalla tahdon lopettaa tämän historiallisen


tutkistelmuksen, jonka hajalliset eriosat vaan ovat näkyneet
odottavan tätä johtopäätöstä, tullaksensa oikeaan valoon. Minä
menen ohitse tuon kertomuksen 16:nessa runossa, joka
silminnähtävästi juttelee merimatkasta pohjaiseen Turjaan, Lappiin ja
Islantiin. Eriasiat etenkin viimemainitusta maasta ovat niin tarkasti
kerrotut, ettei siitä voi pettyä. Kertomus jättiläisvaimosta — Hiigla
tütar = Hekla — joka ottaa uroot esiliinaansa, tavataan Skandinavein
saduissa. Tarkemmat tutkinnot luultavasti tässäkin ovat saattavat
tyydyttävään johtopäätökseen. Tahdon ainoasti vielä sanoa, että kun
8 eli 9 vuosisataa on ennättänyt kulua, matkaan saattamatta mitään
varsinaisempia muutoksia Jämein murteiden kieliopillisessa
rakennuksessa; kun koko tällä aikakaudella niin suuri osa heidän
kertoma-urostarujaan ja jumalaistarullisia luulojansa on tarkassa
muistissa säilynyt, osaksi melkein sanasta sanaan, niin eikö siinä ole
suurin syy, josta voipi päättää asianlaidan olleen saman entisinäkin
aikoina? Suomen suvun karjalainen osa oli paljon etempänä, melkein
tykkänään ulkopuolella Warägein maailmaa. Vielä paljon myöhemmin
yhdistyivät sekä Ruotsalaiset että Wenäläiset Jämein kanssa heitä
vastaan. Ja kuitenkin on laulurikkaus ollut suurinna Karjalaisissa.
Eikö tässä ole osoitus, että heidän tietonsa runoissa ja saduissa
ulottuu monta sataa vuotta taaemmaksi sitä aikaa, josta tässä on
ollut puhe? Vai voisiko kukaan uskoa jonkun kansan sisimmässä
sydämessä syntyvän niin tarkan runollisen käsityksen ja tämän
kansan voivan luoda runollisen kielen sekä eläviä kuvia, ainoastaan
niistä muruista, jotka hänelle tarjotaan vihollistensa keihäistä tahi
jotka kerätään paljaana kaupanvoittona muilta? Minä en sitä usko.

Viiteselitykset:

[1] Verhandl. der Estnischen Gesellschaft I, 1: 15.

[2] Alkulause Kalevipoikaan, Verhandl. IV. 1. kd. Verhandl. III. 78.

[3] Katso toisintoa Europæus'en kirjassa "Pieni Runoseppä" 19.

[4] Kalevipoeg 2; 816.

[5] Kal.poeg 3: 530.


[6] Kal.poeg 3: 570, 767.

[7] Kal.poeg 3: 827.

[8] Kal.-poeg 4: 229.

[9] Vertaa Kal.-poeg 4. Kalevala 29.

[10] Kertomus hänen kuolemastansa, Kal. p. 4: 616, on monessa


kohden samallainen kuin Ainon kuolemasta, koska tämä tahtoi
välttää naimista Wäinämöisen kanssa.

Muun muassa hän laulaa:

*Neiu läks merda kiikumaie,


Laenetesse laulemaie,
Pani kingad kivi peale,
Paatrid pikila pajula,
Siidi lindid liiva peale,
Sõrmuksed sõmera peale.

Kalevalassa 4: 305 sanotaan Ainosta:

Kolme oli neittä niemen päässä,


Ne on merta kylpemässä,
Aino neiti neljänneksi.
Heitti paitansa pajulle,
Kenkänsä vesikivelle,
Helmet hietarantaselle,
Sormukset somerikolle.

[11] Kal.-poeg 5: 404.


[12] Kal.-poeg 5: 682.

[13] Kal.-poeg 6: 60.

[14] Kalevala 34: 70.

[15] Vertaa Kal.p. 6: 640.

[16] Kal.poeg 7: 233.

[17] Kal.-p. 7: 310.

[18] Kal.-poeg 7: 391.

[19] Kalevala 27: 382.

[20] Kalevipoeg 4: 414, 448.

[21] Fählmann, Verh. d. gel. Ehstn. Gesellschaft II, 2, 63.

[22] Ganander, Mythologia Fennica, 29, 86. Lencqvist, De


superstitione vet. Fennorum, 27.

[23] Kal.poeg, 5: 222 y.m.

[24] Kal.poeg 1: 126 seur.

[25] Kanteletar 3, 1.

[26] Kalevala 11 runo.

[27] Kal.poeg 1: 834.

Oh Linda, minu omane!


Mia sina koeo unustid?
Kolmed sa koeo unustid:
Kuu jäi koea lävele,
Se sinu vana isada,
Pääv jäi peale aida viilu,
Se sinu vana onuda.

Oi, Linda minun omani!


Mitäs unhotit kotihin?
Kolmet unhotit kotihin:
Kuu se jäi kodin ovelle,
Se on sun vanha isäsi,
Aurinkoinen aidan päälle,
Se on sun vanha enosi.

Vertaa siihen Kal.poeg 15: 767.

[28] Kal.poeg 17: 110.

[29] Kal.poeg 17: 85.

[30] Kal.poeg 4: 570 seur.

[31] Kal.poeg 5: 222.

[32] Kal.poeg 5: 235.

[33] Kal.poeg 6: 775.

[34] Kal.poeg 6: 895.

[35] Kalevala 2: 201.

[36] Kal.poeg 6: 850 "Kalevi poigia pikuseks".


Welcome to our website – the ideal destination for book lovers and
knowledge seekers. With a mission to inspire endlessly, we offer a
vast collection of books, ranging from classic literary works to
specialized publications, self-development books, and children's
literature. Each book is a new journey of discovery, expanding
knowledge and enriching the soul of the reade

Our website is not just a platform for buying books, but a bridge
connecting readers to the timeless values of culture and wisdom. With
an elegant, user-friendly interface and an intelligent search system,
we are committed to providing a quick and convenient shopping
experience. Additionally, our special promotions and home delivery
services ensure that you save time and fully enjoy the joy of reading.

Let us accompany you on the journey of exploring knowledge and


personal growth!

ebookultra.com

You might also like